diff options
author | Peter Kim <pk15950@gmail.com> | 2022-09-08 07:00:12 +0300 |
---|---|---|
committer | Peter Kim <pk15950@gmail.com> | 2022-09-08 07:00:12 +0300 |
commit | 00dcfdf916c69672210b006e62d966f1bc2fbeb7 (patch) | |
tree | 0cbb1b91fe26c750197126085b74224a795a103c /source/blender/editors/sculpt_paint | |
parent | a39532670f6b668da7be5810fb1f844b82feeba3 (diff) | |
parent | d5934974219135102f364f57c45a8b1465e2b8d9 (diff) |
Merge branch 'master' into xr-devxr-dev
Diffstat (limited to 'source/blender/editors/sculpt_paint')
58 files changed, 6844 insertions, 3249 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index b1b52d16c6d..f4d3002219d 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -21,7 +21,6 @@ set(INC ../../../../intern/atomic ../../../../intern/clog ../../../../intern/eigen - ../../../../intern/glew-mx ../../../../intern/guardedalloc # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna @@ -32,10 +31,15 @@ set(SRC curves_sculpt_brush.cc curves_sculpt_comb.cc curves_sculpt_delete.cc + curves_sculpt_density.cc curves_sculpt_grow_shrink.cc curves_sculpt_ops.cc + curves_sculpt_pinch.cc + curves_sculpt_puff.cc curves_sculpt_selection.cc curves_sculpt_selection_paint.cc + curves_sculpt_slide.cc + curves_sculpt_smooth.cc curves_sculpt_snake_hook.cc paint_canvas.cc paint_cursor.c @@ -52,8 +56,7 @@ set(SRC paint_stroke.c paint_utils.c paint_vertex.cc - paint_vertex_color_ops.c - paint_vertex_color_utils.c + paint_vertex_color_ops.cc paint_vertex_proj.c paint_vertex_weight_ops.c paint_vertex_weight_utils.c diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index f013fa05f4c..b5d739ae08e 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -22,9 +22,11 @@ #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 "BKE_spline.hh" #include "DNA_brush_enums.h" #include "DNA_brush_types.h" @@ -38,10 +40,14 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "GEO_add_curves_on_mesh.hh" + #include "WM_api.h" +#include "DEG_depsgraph_query.h" + /** - * The code below uses a prefix naming convention to indicate the coordinate space: + * The code below uses a suffix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. * su: Local space of the surface object. * wo: World space. @@ -70,16 +76,6 @@ class AddOperation : public CurvesSculptStrokeOperation { void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; -static void initialize_straight_curve_positions(const float3 &p1, - const float3 &p2, - MutableSpan<float3> r_positions) -{ - const float step = 1.0f / (float)(r_positions.size() - 1); - for (const int i : r_positions.index_range()) { - r_positions[i] = math::interpolate(p1, p2, i * step); - } -} - /** * Utility class that actually executes the update when the stroke is updated. That's useful * because it avoids passing a very large number of parameters between functions. @@ -88,60 +84,28 @@ struct AddOperationExecutor { AddOperation *self_ = nullptr; CurvesSculptCommonContext ctx_; - Object *object_ = nullptr; - Curves *curves_id_ = nullptr; - CurvesGeometry *curves_ = nullptr; + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; - Object *surface_ob_ = nullptr; - Mesh *surface_ = nullptr; - Span<MLoopTri> surface_looptris_; - Span<float3> corner_normals_su_; - VArray_Span<float2> surface_uv_map_; + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + Span<MVert> surface_verts_eval_; + Span<MLoop> surface_loops_eval_; + Span<MLoopTri> surface_looptris_eval_; + VArraySpan<float2> surface_uv_map_eval_; + BVHTreeFromMesh surface_bvh_eval_; const CurvesSculpt *curves_sculpt_ = nullptr; const Brush *brush_ = nullptr; const BrushCurvesSculptSettings *brush_settings_ = nullptr; + int add_amount_; + bool use_front_face_; float brush_radius_re_; float2 brush_pos_re_; - bool use_front_face_; - bool interpolate_length_; - bool interpolate_shape_; - bool interpolate_point_count_; - bool use_interpolation_; - float new_curve_length_; - int add_amount_; - int constant_points_per_curve_; - - /** Various matrices to convert between coordinate spaces. */ - float4x4 curves_to_world_mat_; - float4x4 curves_to_surface_mat_; - float4x4 world_to_curves_mat_; - float4x4 world_to_surface_mat_; - float4x4 surface_to_world_mat_; - float4x4 surface_to_curves_mat_; - float4x4 surface_to_curves_normal_mat_; - - BVHTreeFromMesh surface_bvh_; - - int tot_old_curves_; - int tot_old_points_; - - struct AddedPoints { - Vector<float3> positions_cu; - Vector<float3> bary_coords; - Vector<int> looptri_indices; - }; - - struct NeighborInfo { - /* Curve index of the neighbor. */ - int index; - /* The weights of all neighbors of a new curve add up to 1. */ - float weight; - }; - static constexpr int max_neighbors = 5; - using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + CurvesSurfaceTransforms transforms_; AddOperationExecutor(const bContext &C) : ctx_(C) { @@ -150,32 +114,40 @@ struct AddOperationExecutor { void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { self_ = &self; - object_ = CTX_data_active_object(&C); + curves_ob_orig_ = CTX_data_active_object(&C); - curves_id_ = static_cast<Curves *>(object_->data); - curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); - if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) { + if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) { + report_missing_surface(stroke_extension.reports); return; } - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); - surface_ob_ = curves_id_->surface; - surface_ = static_cast<Mesh *>(surface_ob_->data); - surface_to_world_mat_ = surface_ob_->obmat; - world_to_surface_mat_ = surface_to_world_mat_.inverted(); - surface_to_curves_mat_ = world_to_curves_mat_ * surface_to_world_mat_; - surface_to_curves_normal_mat_ = surface_to_curves_mat_.inverted().transposed(); - curves_to_surface_mat_ = world_to_surface_mat_ * curves_to_world_mat_; + Object &surface_ob_orig = *curves_id_orig_->surface; + Mesh &surface_orig = *static_cast<Mesh *>(surface_ob_orig.data); + if (surface_orig.totpoly == 0) { + report_empty_original_surface(stroke_extension.reports); + return; + } - if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_); + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, &surface_ob_orig); + if (surface_ob_eval_ == nullptr) { + return; } - corner_normals_su_ = { - reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), - surface_->totloop}; + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); + if (surface_eval_->totpoly == 0) { + report_empty_evaluated_surface(stroke_extension.reports); + return; + } + 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_)}; + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); @@ -187,109 +159,120 @@ struct AddOperationExecutor { const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); add_amount_ = std::max(0, brush_settings_->add_amount); - constant_points_per_curve_ = std::max(2, brush_settings_->points_per_curve); - interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; - interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; - interpolate_point_count_ = brush_settings_->flag & - BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; - use_interpolation_ = interpolate_length_ || interpolate_shape_ || interpolate_point_count_; - new_curve_length_ = brush_settings_->curve_length; - - tot_old_curves_ = curves_->curves_num(); - tot_old_points_ = curves_->points_num(); if (add_amount_ == 0) { return; } + /* Find UV map. */ + VArraySpan<float2> surface_uv_map; + if (curves_id_orig_->surface_uv_map != nullptr) { + surface_uv_map = surface_orig.attributes().lookup<float2>(curves_id_orig_->surface_uv_map, + ATTR_DOMAIN_CORNER); + surface_uv_map_eval_ = surface_eval_->attributes().lookup<float2>( + curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); + } + + if (surface_uv_map.is_empty()) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (surface_uv_map_eval_.is_empty()) { + report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); + return; + } + const double time = PIL_check_seconds_timer() * 1000000.0; /* Use a pointer cast to avoid overflow warnings. */ RandomNumberGenerator rng{*(uint32_t *)(&time)}; - BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); - BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); - - surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), - BKE_mesh_runtime_looptri_len(surface_)}; - - if (curves_id_->surface_uv_map != nullptr) { - MeshComponent surface_component; - surface_component.replace(surface_, GeometryOwnershipType::ReadOnly); - surface_uv_map_ = surface_component - .attribute_try_get_for_read(curves_id_->surface_uv_map, - ATTR_DOMAIN_CORNER) - .typed<float2>(); - } - /* Sample points on the surface using one of multiple strategies. */ - AddedPoints added_points; + Vector<float2> sampled_uvs; if (add_amount_ == 1) { - this->sample_in_center_with_symmetry(added_points); + this->sample_in_center_with_symmetry(sampled_uvs); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->sample_projected_with_symmetry(rng, added_points); + this->sample_projected_with_symmetry(rng, sampled_uvs); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->sample_spherical_with_symmetry(rng, added_points); + this->sample_spherical_with_symmetry(rng, sampled_uvs); } else { BLI_assert_unreachable(); } - if (added_points.bary_coords.is_empty()) { + if (sampled_uvs.is_empty()) { /* No new points have been added. */ return; } - Array<NeighborsVector> neighbors_per_curve; - if (use_interpolation_) { - this->ensure_curve_roots_kdtree(); - neighbors_per_curve = this->find_curve_neighbors(added_points); - } + const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(&surface_orig), + BKE_mesh_runtime_looptri_len(&surface_orig)}; - /* Resize to add the new curves, building the offsets in the array owned by the curves. */ - const int tot_added_curves = added_points.bary_coords.size(); - curves_->resize(curves_->points_num(), curves_->curves_num() + tot_added_curves); - if (interpolate_point_count_) { - this->initialize_curve_offsets_with_interpolation(neighbors_per_curve); + /* Find normals. */ + if (!CustomData_has_layer(&surface_orig.ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(&surface_orig); } - else { - this->initialize_curve_offsets_without_interpolation(constant_points_per_curve_); + const Span<float3> corner_normals_su = { + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig.ldata, CD_NORMAL)), + surface_orig.totloop}; + + const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig}; + + geometry::AddCurvesOnMeshInputs add_inputs; + add_inputs.uvs = sampled_uvs; + add_inputs.interpolate_length = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; + add_inputs.interpolate_shape = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; + add_inputs.interpolate_point_count = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; + add_inputs.fallback_curve_length = brush_settings_->curve_length; + add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve); + add_inputs.transforms = &transforms_; + add_inputs.reverse_uv_sampler = &reverse_uv_sampler; + add_inputs.surface = &surface_orig; + add_inputs.corner_normals_su = corner_normals_su; + + if (add_inputs.interpolate_length || add_inputs.interpolate_shape || + add_inputs.interpolate_point_count) { + this->ensure_curve_roots_kdtree(); + add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_; } - /* Resize to add the correct point count calculated as part of building the offsets. */ - curves_->resize(curves_->offsets().last(), curves_->curves_num()); + const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh( + *curves_orig_, add_inputs); - this->initialize_attributes(added_points, neighbors_per_curve); - - curves_->update_curve_types(); + if (add_outputs.uv_error) { + report_invalid_uv_map(stroke_extension.reports); + } - DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); ED_region_tag_redraw(ctx_.region); } /** * Sample a single point exactly at the mouse position. */ - void sample_in_center_with_symmetry(AddedPoints &r_added_points) + void sample_in_center_with_symmetry(Vector<float2> &r_sampled_uvs) { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( ctx_.depsgraph, ctx_.region, ctx_.v3d, brush_pos_re_, ray_start_wo, ray_end_wo, true); - const float3 ray_start_cu = world_to_curves_mat_ * ray_start_wo; - const float3 ray_end_cu = world_to_curves_mat_ * ray_end_wo; + const float3 ray_start_cu = transforms_.world_to_curves * ray_start_wo; + const float3 ray_end_cu = transforms_.world_to_curves * ray_end_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - const float4x4 transform = curves_to_surface_mat_ * brush_transform; - this->sample_in_center(r_added_points, transform * ray_start_cu, transform * ray_end_cu); + const float4x4 transform = transforms_.curves_to_surface * brush_transform; + this->sample_in_center(r_sampled_uvs, transform * ray_start_cu, transform * ray_end_cu); } } - void sample_in_center(AddedPoints &r_added_points, + void sample_in_center(Vector<float2> &r_sampled_uvs, const float3 &ray_start_su, const float3 &ray_end_su) { @@ -298,204 +281,165 @@ struct AddOperationExecutor { BVHTreeRayHit ray_hit; ray_hit.dist = FLT_MAX; ray_hit.index = -1; - BLI_bvhtree_ray_cast(surface_bvh_.tree, + BLI_bvhtree_ray_cast(surface_bvh_eval_.tree, ray_start_su, ray_direction_su, 0.0f, &ray_hit, - surface_bvh_.raycast_callback, - &surface_bvh_); + surface_bvh_eval_.raycast_callback, + &surface_bvh_eval_); if (ray_hit.index == -1) { return; } const int looptri_index = ray_hit.index; + const MLoopTri &looptri = surface_looptris_eval_[looptri_index]; const float3 brush_pos_su = ray_hit.co; - const float3 bary_coords = compute_bary_coord_in_triangle( - *surface_, surface_looptris_[looptri_index], brush_pos_su); + const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle( + surface_verts_eval_, surface_loops_eval_, looptri, brush_pos_su); - const float3 brush_pos_cu = surface_to_curves_mat_ * brush_pos_su; - - r_added_points.positions_cu.append(brush_pos_cu); - r_added_points.bary_coords.append(bary_coords); - r_added_points.looptri_indices.append(looptri_index); + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords, looptri, surface_uv_map_eval_); + r_sampled_uvs.append(uv); } /** * Sample points by shooting rays within the brush radius in the 3D view. */ - void sample_projected_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_projected_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs) { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->sample_projected(rng, r_added_points, brush_transform); + this->sample_projected(rng, r_sampled_uvs, brush_transform); } } void sample_projected(RandomNumberGenerator &rng, - AddedPoints &r_added_points, + Vector<float2> &r_sampled_uvs, const float4x4 &brush_transform) { - const int old_amount = r_added_points.bary_coords.size(); - const int max_iterations = std::max(100'000, add_amount_ * 10); + const int old_amount = r_sampled_uvs.size(); + const int max_iterations = 100; int current_iteration = 0; - while (r_added_points.bary_coords.size() < old_amount + add_amount_) { + while (r_sampled_uvs.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } - - const float r = brush_radius_re_ * std::sqrt(rng.get_float()); - const float angle = rng.get_float() * 2.0f * M_PI; - const float2 pos_re = brush_pos_re_ + r * float2(std::cos(angle), std::sin(angle)); - - float3 ray_start_wo, ray_end_wo; - ED_view3d_win_to_segment_clipped( - ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, ray_start_wo, ray_end_wo, true); - const float3 ray_start_cu = brush_transform * (world_to_curves_mat_ * ray_start_wo); - const float3 ray_end_cu = brush_transform * (world_to_curves_mat_ * ray_end_wo); - - const float3 ray_start_su = curves_to_surface_mat_ * ray_start_cu; - const float3 ray_end_su = curves_to_surface_mat_ * ray_end_cu; - const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); - - BVHTreeRayHit ray_hit; - ray_hit.dist = FLT_MAX; - ray_hit.index = -1; - BLI_bvhtree_ray_cast(surface_bvh_.tree, - ray_start_su, - ray_direction_su, - 0.0f, - &ray_hit, - surface_bvh_.raycast_callback, - &surface_bvh_); - - if (ray_hit.index == -1) { - continue; - } - - if (use_front_face_) { - const float3 normal_su = ray_hit.no; - if (math::dot(ray_direction_su, normal_su) >= 0.0f) { - continue; - } + Vector<float3> bary_coords; + Vector<int> looptri_indices; + Vector<float3> positions_su; + + const int missing_amount = add_amount_ + old_amount - r_sampled_uvs.size(); + const int new_points = bke::mesh_surface_sample::sample_surface_points_projected( + rng, + *surface_eval_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re_, + [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) { + float3 start_wo, end_wo; + ED_view3d_win_to_segment_clipped( + ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true); + const float3 start_cu = brush_transform * (transforms_.world_to_curves * start_wo); + const float3 end_cu = brush_transform * (transforms_.world_to_curves * end_wo); + r_start_su = transforms_.curves_to_surface * start_cu; + r_end_su = transforms_.curves_to_surface * end_cu; + }, + use_front_face_, + add_amount_, + missing_amount, + bary_coords, + looptri_indices, + positions_su); + + for (const int i : IndexRange(new_points)) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_sampled_uvs.append(uv); } - - const int looptri_index = ray_hit.index; - const float3 pos_su = ray_hit.co; - - const float3 bary_coords = compute_bary_coord_in_triangle( - *surface_, surface_looptris_[looptri_index], pos_su); - - const float3 pos_cu = surface_to_curves_mat_ * pos_su; - - r_added_points.positions_cu.append(pos_cu); - r_added_points.bary_coords.append(bary_coords); - r_added_points.looptri_indices.append(looptri_index); } } /** * Sample points in a 3D sphere around the surface position that the mouse hovers over. */ - void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_spherical_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs) { - /* Find ray that starts in the center of the brush. */ - float3 brush_ray_start_wo, brush_ray_end_wo; + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + transforms_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re_); + if (!brush_3d.has_value()) { + return; + } + + float3 view_ray_start_wo, view_ray_end_wo; ED_view3d_win_to_segment_clipped(ctx_.depsgraph, ctx_.region, ctx_.v3d, brush_pos_re_, - brush_ray_start_wo, - brush_ray_end_wo, + view_ray_start_wo, + view_ray_end_wo, true); - const float3 brush_ray_start_cu = world_to_curves_mat_ * brush_ray_start_wo; - const float3 brush_ray_end_cu = world_to_curves_mat_ * brush_ray_end_wo; - /* Find ray that starts on the boundary of the brush. That is used to compute the brush radius - * in 3D. */ - float3 brush_radius_ray_start_wo, brush_radius_ray_end_wo; - ED_view3d_win_to_segment_clipped(ctx_.depsgraph, - ctx_.region, - ctx_.v3d, - brush_pos_re_ + float2(brush_radius_re_, 0), - brush_radius_ray_start_wo, - brush_radius_ray_end_wo, - true); - const float3 brush_radius_ray_start_cu = world_to_curves_mat_ * brush_radius_ray_start_wo; - const float3 brush_radius_ray_end_cu = world_to_curves_mat_ * brush_radius_ray_end_wo; + const float3 view_ray_start_cu = transforms_.world_to_curves * view_ray_start_wo; + const float3 view_ray_end_cu = transforms_.world_to_curves * view_ray_end_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - const float4x4 transform = curves_to_surface_mat_ * brush_transform; - this->sample_spherical(rng, - r_added_points, - transform * brush_ray_start_cu, - transform * brush_ray_end_cu, - transform * brush_radius_ray_start_cu, - transform * brush_radius_ray_end_cu); + const float4x4 transform = transforms_.curves_to_surface * brush_transform; + + const float3 brush_pos_su = transform * brush_3d->position_cu; + const float3 view_direction_su = math::normalize(transform * view_ray_end_cu - + transform * view_ray_start_cu); + const float brush_radius_su = transform_brush_radius( + transform, brush_3d->position_cu, brush_3d->radius_cu); + + this->sample_spherical(rng, r_sampled_uvs, brush_pos_su, brush_radius_su, view_direction_su); } } void sample_spherical(RandomNumberGenerator &rng, - AddedPoints &r_added_points, - const float3 &brush_ray_start_su, - const float3 &brush_ray_end_su, - const float3 &brush_radius_ray_start_su, - const float3 &brush_radius_ray_end_su) + Vector<float2> &r_sampled_uvs, + const float3 &brush_pos_su, + const float brush_radius_su, + const float3 &view_direction_su) { - const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); - - BVHTreeRayHit ray_hit; - ray_hit.dist = FLT_MAX; - ray_hit.index = -1; - BLI_bvhtree_ray_cast(surface_bvh_.tree, - brush_ray_start_su, - brush_ray_direction_su, - 0.0f, - &ray_hit, - surface_bvh_.raycast_callback, - &surface_bvh_); - - if (ray_hit.index == -1) { - return; - } - - /* Compute brush radius. */ - const float3 brush_pos_su = ray_hit.co; - const float brush_radius_su = dist_to_line_v3( - brush_pos_su, brush_radius_ray_start_su, brush_radius_ray_end_su); const float brush_radius_sq_su = pow2f(brush_radius_su); /* Find surface triangles within brush radius. */ - Vector<int> looptri_indices; + Vector<int> selected_looptri_indices; if (use_front_face_) { BLI_bvhtree_range_query_cpp( - *surface_bvh_.tree, + *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { - const MLoopTri &looptri = surface_looptris_[index]; - const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co; - const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co; - const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; + 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; + const float3 v2_su = surface_verts_eval_[surface_loops_eval_[looptri.tri[2]].v].co; float3 normal_su; normal_tri_v3(normal_su, v0_su, v1_su, v2_su); - if (math::dot(normal_su, brush_ray_direction_su) >= 0.0f) { + if (math::dot(normal_su, view_direction_su) >= 0.0f) { return; } - looptri_indices.append(index); + selected_looptri_indices.append(index); }); } else { BLI_bvhtree_range_query_cpp( - *surface_bvh_.tree, + *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { - looptri_indices.append(index); + selected_looptri_indices.append(index); }); } @@ -505,420 +449,54 @@ struct AddOperationExecutor { const float brush_plane_area_su = M_PI * brush_radius_sq_su; const float approximate_density_su = add_amount_ / brush_plane_area_su; - /* Used for switching between two triangle sampling strategies. */ - const float area_threshold = brush_plane_area_su; - /* Usually one or two iterations should be enough. */ const int max_iterations = 5; int current_iteration = 0; - const int old_amount = r_added_points.bary_coords.size(); - while (r_added_points.bary_coords.size() < old_amount + add_amount_) { + const int old_amount = r_sampled_uvs.size(); + while (r_sampled_uvs.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } - - for (const int looptri_index : looptri_indices) { - const MLoopTri &looptri = surface_looptris_[looptri_index]; - - const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co; - const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co; - const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; - - const float looptri_area_su = area_tri_v3(v0_su, v1_su, v2_su); - - if (looptri_area_su < area_threshold) { - /* The triangle is small compared to the brush radius. Sample by generating random - * barycentric coordinates. */ - const int amount = rng.round_probabilistic(approximate_density_su * looptri_area_su); - for ([[maybe_unused]] const int i : IndexRange(amount)) { - const float3 bary_coord = rng.get_barycentric_coordinates(); - const float3 point_pos_su = attribute_math::mix3(bary_coord, v0_su, v1_su, v2_su); - const float distance_to_brush_sq_su = math::distance_squared(point_pos_su, - brush_pos_su); - if (distance_to_brush_sq_su > brush_radius_sq_su) { - continue; - } - - r_added_points.bary_coords.append(bary_coord); - r_added_points.looptri_indices.append(looptri_index); - r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su); - } - } - else { - /* The triangle is large compared to the brush radius. Sample by generating random points - * on the triangle plane within the brush radius. */ - float3 normal_su; - normal_tri_v3(normal_su, v0_su, v1_su, v2_su); - - float3 brush_pos_proj_su = brush_pos_su; - project_v3_plane(brush_pos_proj_su, normal_su, v0_su); - - const float proj_distance_sq_su = math::distance_squared(brush_pos_proj_su, - brush_pos_su); - const float brush_radius_factor_sq = 1.0f - - std::min(1.0f, - proj_distance_sq_su / brush_radius_sq_su); - const float radius_proj_sq_su = brush_radius_sq_su * brush_radius_factor_sq; - const float radius_proj_su = std::sqrt(radius_proj_sq_su); - const float circle_area_su = M_PI * radius_proj_su; - - const int amount = rng.round_probabilistic(approximate_density_su * circle_area_su); - - const float3 axis_1_su = math::normalize(v1_su - v0_su) * radius_proj_su; - const float3 axis_2_su = math::normalize(math::cross( - axis_1_su, math::cross(axis_1_su, v2_su - v0_su))) * - radius_proj_su; - - for ([[maybe_unused]] const int i : IndexRange(amount)) { - const float r = std::sqrt(rng.get_float()); - const float angle = rng.get_float() * 2.0f * M_PI; - const float x = r * std::cos(angle); - const float y = r * std::sin(angle); - const float3 point_pos_su = brush_pos_proj_su + axis_1_su * x + axis_2_su * y; - if (!isect_point_tri_prism_v3(point_pos_su, v0_su, v1_su, v2_su)) { - /* Sampled point is not in the triangle. */ - continue; - } - - float3 bary_coord; - interp_weights_tri_v3(bary_coord, v0_su, v1_su, v2_su, point_pos_su); - - r_added_points.bary_coords.append(bary_coord); - r_added_points.looptri_indices.append(looptri_index); - r_added_points.positions_cu.append(surface_to_curves_mat_ * point_pos_su); - } - } + Vector<float3> bary_coords; + Vector<int> looptri_indices; + Vector<float3> positions_su; + const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical( + rng, + *surface_eval_, + selected_looptri_indices, + brush_pos_su, + brush_radius_su, + approximate_density_su, + bary_coords, + looptri_indices, + positions_su); + for (const int i : IndexRange(new_points)) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_sampled_uvs.append(uv); } } /* Remove samples when there are too many. */ - while (r_added_points.bary_coords.size() > old_amount + add_amount_) { + while (r_sampled_uvs.size() > old_amount + add_amount_) { const int index_to_remove = rng.get_int32(add_amount_) + old_amount; - r_added_points.bary_coords.remove_and_reorder(index_to_remove); - r_added_points.looptri_indices.remove_and_reorder(index_to_remove); - r_added_points.positions_cu.remove_and_reorder(index_to_remove); + r_sampled_uvs.remove_and_reorder(index_to_remove); } } void ensure_curve_roots_kdtree() { if (self_->curve_roots_kdtree_ == nullptr) { - self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_->curves_num()); - for (const int curve_i : curves_->curves_range()) { - const int root_point_i = curves_->offsets()[curve_i]; - const float3 &root_pos_cu = curves_->positions()[root_point_i]; + self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_orig_->curves_num()); + for (const int curve_i : curves_orig_->curves_range()) { + const int root_point_i = curves_orig_->offsets()[curve_i]; + const float3 &root_pos_cu = curves_orig_->positions()[root_point_i]; BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu); } BLI_kdtree_3d_balance(self_->curve_roots_kdtree_); } } - - void initialize_curve_offsets_with_interpolation(const Span<NeighborsVector> neighbors_per_curve) - { - MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); - - attribute_math::DefaultMixer<int> mixer{new_offsets}; - threading::parallel_for(neighbors_per_curve.index_range(), 1024, [&](IndexRange curves_range) { - for (const int i : curves_range) { - if (neighbors_per_curve[i].is_empty()) { - mixer.mix_in(i, constant_points_per_curve_, 1.0f); - } - else { - for (const NeighborInfo &neighbor : neighbors_per_curve[i]) { - const int neighbor_points_num = curves_->points_for_curve(neighbor.index).size(); - mixer.mix_in(i, neighbor_points_num, neighbor.weight); - } - } - } - }); - mixer.finalize(); - - bke::curves::accumulate_counts_to_offsets(new_offsets, tot_old_points_); - } - - void initialize_curve_offsets_without_interpolation(const int points_per_curve) - { - MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); - int offset = tot_old_points_; - for (const int i : new_offsets.index_range()) { - new_offsets[i] = offset; - offset += points_per_curve; - } - } - - void initialize_attributes(const AddedPoints &added_points, - const Span<NeighborsVector> neighbors_per_curve) - { - Array<float> new_lengths_cu(added_points.bary_coords.size()); - if (interpolate_length_) { - this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); - } - else { - new_lengths_cu.fill(new_curve_length_); - } - - Array<float3> new_normals_su = this->compute_normals_for_added_curves_su(added_points); - if (!surface_uv_map_.is_empty()) { - this->initialize_surface_attachment(added_points); - } - - this->fill_new_selection(); - - if (interpolate_shape_) { - this->initialize_position_with_interpolation( - added_points, neighbors_per_curve, new_normals_su, new_lengths_cu); - } - else { - this->initialize_position_without_interpolation( - added_points, new_lengths_cu, new_normals_su); - } - } - - /** - * Select newly created points or curves in new curves if necessary. - */ - void fill_new_selection() - { - switch (curves_id_->selection_domain) { - case ATTR_DOMAIN_CURVE: { - const VArray<float> selection = curves_->selection_curve_float(); - if (selection.is_single() && selection.get_internal_single() >= 1.0f) { - return; - } - curves_->selection_curve_float_for_write().drop_front(tot_old_curves_).fill(1.0f); - break; - } - case ATTR_DOMAIN_POINT: { - const VArray<float> selection = curves_->selection_point_float(); - if (selection.is_single() && selection.get_internal_single() >= 1.0f) { - return; - } - curves_->selection_point_float_for_write().drop_front(tot_old_points_).fill(1.0f); - break; - } - default: - BLI_assert_unreachable(); - } - } - - Array<NeighborsVector> find_curve_neighbors(const AddedPoints &added_points) - { - const int tot_added_curves = added_points.bary_coords.size(); - Array<NeighborsVector> neighbors_per_curve(tot_added_curves); - threading::parallel_for(IndexRange(tot_added_curves), 128, [&](const IndexRange range) { - for (const int i : range) { - const float3 root_cu = added_points.positions_cu[i]; - std::array<KDTreeNearest_3d, max_neighbors> nearest_n; - const int found_neighbors = BLI_kdtree_3d_find_nearest_n( - self_->curve_roots_kdtree_, root_cu, nearest_n.data(), max_neighbors); - float tot_weight = 0.0f; - for (const int neighbor_i : IndexRange(found_neighbors)) { - KDTreeNearest_3d &nearest = nearest_n[neighbor_i]; - const float weight = 1.0f / std::max(nearest.dist, 0.00001f); - tot_weight += weight; - neighbors_per_curve[i].append({nearest.index, weight}); - } - /* Normalize weights. */ - for (NeighborInfo &neighbor : neighbors_per_curve[i]) { - neighbor.weight /= tot_weight; - } - } - }); - return neighbors_per_curve; - } - - void interpolate_lengths(const Span<NeighborsVector> neighbors_per_curve, - MutableSpan<float> r_lengths) - { - const Span<float3> positions_cu = curves_->positions(); - - threading::parallel_for(r_lengths.index_range(), 128, [&](const IndexRange range) { - for (const int added_curve_i : range) { - const Span<NeighborInfo> neighbors = neighbors_per_curve[added_curve_i]; - float length_sum = 0.0f; - for (const NeighborInfo &neighbor : neighbors) { - const IndexRange neighbor_points = curves_->points_for_curve(neighbor.index); - float neighbor_length = 0.0f; - for (const int segment_i : neighbor_points.drop_back(1)) { - const float3 &p1 = positions_cu[segment_i]; - const float3 &p2 = positions_cu[segment_i + 1]; - neighbor_length += math::distance(p1, p2); - } - length_sum += neighbor.weight * neighbor_length; - } - const float length = neighbors.is_empty() ? new_curve_length_ : length_sum; - r_lengths[added_curve_i] = length; - } - }); - } - - float3 compute_point_normal_su(const int looptri_index, const float3 &bary_coord) - { - const MLoopTri &looptri = surface_looptris_[looptri_index]; - const int l0 = looptri.tri[0]; - const int l1 = looptri.tri[1]; - const int l2 = looptri.tri[2]; - - const float3 &l0_normal_su = corner_normals_su_[l0]; - const float3 &l1_normal_su = corner_normals_su_[l1]; - const float3 &l2_normal_su = corner_normals_su_[l2]; - - const float3 normal_su = math::normalize( - attribute_math::mix3(bary_coord, l0_normal_su, l1_normal_su, l2_normal_su)); - return normal_su; - } - - Array<float3> compute_normals_for_added_curves_su(const AddedPoints &added_points) - { - Array<float3> normals_su(added_points.bary_coords.size()); - threading::parallel_for(normals_su.index_range(), 256, [&](const IndexRange range) { - for (const int i : range) { - const int looptri_index = added_points.looptri_indices[i]; - const float3 &bary_coord = added_points.bary_coords[i]; - normals_su[i] = compute_surface_point_normal( - surface_looptris_[looptri_index], bary_coord, corner_normals_su_); - } - }); - return normals_su; - } - - void initialize_surface_attachment(const AddedPoints &added_points) - { - MutableSpan<float2> surface_uv_coords = curves_->surface_uv_coords_for_write(); - threading::parallel_for( - added_points.bary_coords.index_range(), 1024, [&](const IndexRange range) { - for (const int i : range) { - const int curve_i = tot_old_curves_ + i; - const MLoopTri &looptri = surface_looptris_[added_points.looptri_indices[i]]; - const float2 &uv0 = surface_uv_map_[looptri.tri[0]]; - const float2 &uv1 = surface_uv_map_[looptri.tri[1]]; - const float2 &uv2 = surface_uv_map_[looptri.tri[2]]; - const float3 &bary_coords = added_points.bary_coords[i]; - const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2); - surface_uv_coords[curve_i] = uv; - } - }); - } - - /** - * Initialize new curves so that they are just a straight line in the normal direction. - */ - void initialize_position_without_interpolation(const AddedPoints &added_points, - const Span<float> lengths_cu, - const MutableSpan<float3> normals_su) - { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - - threading::parallel_for( - added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { - for (const int i : range) { - const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); - const float3 &root_cu = added_points.positions_cu[i]; - const float length = lengths_cu[i]; - const float3 &normal_su = normals_su[i]; - const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); - const float3 tip_cu = root_cu + length * normal_cu; - - initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); - } - }); - } - - /** - * Use neighboring curves to determine the shape. - */ - void initialize_position_with_interpolation(const AddedPoints &added_points, - const Span<NeighborsVector> neighbors_per_curve, - const Span<float3> new_normals_su, - const Span<float> new_lengths_cu) - { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - - threading::parallel_for( - added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { - for (const int i : range) { - const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; - const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); - - const float length_cu = new_lengths_cu[i]; - const float3 &normal_su = new_normals_su[i]; - const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); - - const float3 &root_cu = added_points.positions_cu[i]; - - if (neighbors.is_empty()) { - /* If there are no neighbors, just make a straight line. */ - const float3 tip_cu = root_cu + length_cu * normal_cu; - initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); - continue; - } - - positions_cu.slice(points).fill(root_cu); - - for (const NeighborInfo &neighbor : neighbors) { - const int neighbor_curve_i = neighbor.index; - const float3 &neighbor_first_pos_cu = - positions_cu[curves_->offsets()[neighbor_curve_i]]; - const float3 neighbor_first_pos_su = curves_to_surface_mat_ * neighbor_first_pos_cu; - - BVHTreeNearest nearest; - nearest.dist_sq = FLT_MAX; - BLI_bvhtree_find_nearest(surface_bvh_.tree, - neighbor_first_pos_su, - &nearest, - surface_bvh_.nearest_callback, - &surface_bvh_); - const int neighbor_looptri_index = nearest.index; - const MLoopTri &neighbor_looptri = surface_looptris_[neighbor_looptri_index]; - - const float3 neighbor_bary_coord = compute_bary_coord_in_triangle( - *surface_, neighbor_looptri, nearest.co); - - const float3 neighbor_normal_su = compute_surface_point_normal( - surface_looptris_[neighbor_looptri_index], - neighbor_bary_coord, - corner_normals_su_); - const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat_ * - neighbor_normal_su); - - /* The rotation matrix used to transform relative coordinates of the neighbor curve - * to the new curve. */ - float normal_rotation_cu[3][3]; - rotation_between_vecs_to_mat3(normal_rotation_cu, neighbor_normal_cu, normal_cu); - - const IndexRange neighbor_points = curves_->points_for_curve(neighbor_curve_i); - const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]]; - - /* Use a temporary #PolySpline, because that's the easiest way to resample an - * existing curve right now. Resampling is necessary if the length of the new curve - * does not match the length of the neighbors or the number of handle points is - * different. */ - PolySpline neighbor_spline; - neighbor_spline.resize(neighbor_points.size()); - neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points)); - neighbor_spline.mark_cache_invalid(); - - const float neighbor_length_cu = neighbor_spline.length(); - const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); - - const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor; - for (const int j : IndexRange(points.size())) { - const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( - j * resample_factor); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - neighbor_spline.sample_with_index_factors<float3>( - neighbor_spline.positions(), {&index_factor, 1}, {&p, 1}); - const float3 relative_coord = p - neighbor_root_cu; - float3 rotated_relative_coord = relative_coord; - mul_m3_v3(normal_rotation_cu, rotated_relative_coord); - positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; - } - } - } - }); - } }; void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) @@ -927,17 +505,8 @@ void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension & executor.execute(*this, C, stroke_extension); } -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, - ReportList *reports) +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation() { - const Object &ob_active = *CTX_data_active_object(&C); - BLI_assert(ob_active.type == OB_CURVES); - const Curves &curves_id = *static_cast<Curves *>(ob_active.data); - if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) { - BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh"); - return {}; - } - return std::make_unique<AddOperation>(); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index 7e583773512..95261f29914 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -8,8 +8,9 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" - -#include "DNA_meshdata_types.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_report.h" #include "ED_view3d.h" @@ -22,6 +23,10 @@ #include "BLI_length_parameterize.hh" #include "BLI_task.hh" +#include "DEG_depsgraph_query.h" + +#include "BLT_translation.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -50,7 +55,8 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const float brush_radius_re, const ARegion ®ion, const RegionView3D &rv3d, - const Object &object) + const Object &object, + 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); @@ -90,8 +96,6 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu } }; - const Span<float3> positions = curves.positions(); - BrushPositionCandidate best_candidate = threading::parallel_reduce( curves.curves_range(), 128, @@ -177,20 +181,21 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, { const Curves &curves_id = *static_cast<Curves *>(curves_object.data); const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - const Object *surface_object = curves_id.surface; + Object *surface_object = curves_id.surface; + Object *surface_object_eval = DEG_get_evaluated_object(&depsgraph, surface_object); float3 center_ray_start_wo, center_ray_end_wo; ED_view3d_win_to_segment_clipped( &depsgraph, ®ion, &v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); /* Shorten ray when the surface object is hit. */ - if (surface_object != nullptr) { + if (surface_object_eval != nullptr) { const float4x4 surface_to_world_mat = surface_object->obmat; const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); - Mesh &surface = *static_cast<Mesh *>(surface_object->data); + Mesh *surface_eval = BKE_object_get_evaluated_mesh(surface_object_eval); BVHTreeFromMesh surface_bvh; - BKE_bvhtree_from_mesh_get(&surface_bvh, &surface, BVHTREE_FROM_LOOPTRI, 2); + BKE_bvhtree_from_mesh_get(&surface_bvh, surface_eval, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); const float3 center_ray_start_su = world_to_surface_mat * center_ray_start_wo; @@ -224,6 +229,9 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, const float3 center_ray_start_cu = world_to_curves_mat * center_ray_start_wo; const float3 center_ray_end_cu = world_to_curves_mat * center_ray_end_wo; + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_object); + const std::optional<float3> brush_position_optional_cu = find_curves_brush_position( curves, center_ray_start_cu, @@ -231,7 +239,8 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, brush_radius_re, region, rv3d, - curves_object); + curves_object, + deformation.positions); if (!brush_position_optional_cu.has_value()) { /* Nothing found. */ return std::nullopt; @@ -256,6 +265,55 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, return brush_3d; } +std::optional<CurvesBrush3D> sample_curves_surface_3d_brush( + const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const CurvesSurfaceTransforms &transforms, + const BVHTreeFromMesh &surface_bvh, + const float2 &brush_pos_re, + const float brush_radius_re) +{ + float3 brush_ray_start_wo, brush_ray_end_wo; + ED_view3d_win_to_segment_clipped( + &depsgraph, ®ion, &v3d, brush_pos_re, brush_ray_start_wo, brush_ray_end_wo, true); + const float3 brush_ray_start_su = transforms.world_to_surface * brush_ray_start_wo; + const float3 brush_ray_end_su = transforms.world_to_surface * brush_ray_end_wo; + + const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); + + BVHTreeRayHit ray_hit; + ray_hit.dist = FLT_MAX; + ray_hit.index = -1; + BLI_bvhtree_ray_cast(surface_bvh.tree, + brush_ray_start_su, + brush_ray_direction_su, + 0.0f, + &ray_hit, + surface_bvh.raycast_callback, + const_cast<void *>(static_cast<const void *>(&surface_bvh))); + if (ray_hit.index == -1) { + return std::nullopt; + } + + float3 brush_radius_ray_start_wo, brush_radius_ray_end_wo; + ED_view3d_win_to_segment_clipped(&depsgraph, + ®ion, + &v3d, + brush_pos_re + float2(brush_radius_re, 0), + brush_radius_ray_start_wo, + brush_radius_ray_end_wo, + true); + const float3 brush_radius_ray_start_cu = transforms.world_to_curves * brush_radius_ray_start_wo; + const float3 brush_radius_ray_end_cu = transforms.world_to_curves * brush_radius_ray_end_wo; + + const float3 brush_pos_su = ray_hit.co; + const float3 brush_pos_cu = transforms.surface_to_curves * brush_pos_su; + const float brush_radius_cu = dist_to_line_v3( + brush_pos_cu, brush_radius_ray_start_cu, brush_radius_ray_end_cu); + return CurvesBrush3D{brush_pos_cu, brush_radius_cu}; +} + Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry) { Vector<float4x4> matrices; @@ -284,11 +342,21 @@ Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetr return matrices; } +float transform_brush_radius(const float4x4 &transform, + const float3 &brush_position, + const float old_radius) +{ + const float3 offset_position = brush_position + float3(old_radius, 0.0f, 0.0f); + const float3 new_position = transform * brush_position; + const float3 new_offset_position = transform * offset_position; + return math::distance(new_position, new_offset_position); +} + void move_last_point_and_resample(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::lengths_num(positions.size(), false)); + 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(); @@ -306,11 +374,10 @@ void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &n Array<int> indices(positions.size() - 1); Array<float> factors(positions.size() - 1); - length_parameterize::create_samples_from_sorted_lengths( - orig_lengths, new_lengths, false, indices, factors); + length_parameterize::sample_at_lengths(orig_lengths, new_lengths, indices, factors); Array<float3> new_positions(positions.size() - 1); - length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions); + length_parameterize::interpolate<float3>(positions, indices, factors, new_positions); positions.drop_back(1).copy_from(new_positions); positions.last() = new_last_position; } @@ -324,33 +391,36 @@ CurvesSculptCommonContext::CurvesSculptCommonContext(const bContext &C) this->rv3d = CTX_wm_region_view3d(&C); } -float3 compute_surface_point_normal(const MLoopTri &looptri, - const float3 &bary_coord, - const Span<float3> corner_normals) +void report_empty_original_surface(ReportList *reports) { - const int l0 = looptri.tri[0]; - const int l1 = looptri.tri[1]; - const int l2 = looptri.tri[2]; + BKE_report(reports, RPT_WARNING, TIP_("Original surface mesh is empty")); +} - const float3 &l0_normal = corner_normals[l0]; - const float3 &l1_normal = corner_normals[l1]; - const float3 &l2_normal = corner_normals[l2]; +void report_empty_evaluated_surface(ReportList *reports) +{ + BKE_report(reports, RPT_WARNING, TIP_("Evaluated surface mesh is empty")); +} - const float3 normal = math::normalize( - attribute_math::mix3(bary_coord, l0_normal, l1_normal, l2_normal)); - return normal; +void report_missing_surface(ReportList *reports) +{ + BKE_report(reports, RPT_WARNING, TIP_("Missing surface mesh")); +} + +void report_missing_uv_map_on_original_surface(ReportList *reports) +{ + BKE_report( + reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on original surface")); +} + +void report_missing_uv_map_on_evaluated_surface(ReportList *reports) +{ + BKE_report( + reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on evaluated surface")); } -float3 compute_bary_coord_in_triangle(const Mesh &mesh, - const MLoopTri &looptri, - const float3 &position) +void report_invalid_uv_map(ReportList *reports) { - const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co; - const float3 &v1 = mesh.mvert[mesh.mloop[looptri.tri[1]].v].co; - const float3 &v2 = mesh.mvert[mesh.mloop[looptri.tri[2]].v].co; - float3 bary_coords; - interp_weights_tri_v3(bary_coords, v0, v1, v2, position); - return bary_coords; + BKE_report(reports, RPT_WARNING, TIP_("Invalid UV map: UV islands must not overlap")); } } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index ae0a512c5ee..52f2ddc6550 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -13,12 +13,15 @@ #include "PIL_time.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "BKE_attribute_math.hh" #include "BKE_brush.h" #include "BKE_bvhutils.h" #include "BKE_context.h" +#include "BKE_crazyspace.hh" #include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" @@ -88,9 +91,9 @@ struct CombOperationExecutor { eBrushFalloffShape falloff_shape_; - Object *object_ = nullptr; - Curves *curves_id_ = nullptr; - CurvesGeometry *curves_ = nullptr; + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; VArray<float> point_factors_; Vector<int64_t> selected_curve_indices_; @@ -100,8 +103,7 @@ struct CombOperationExecutor { float2 brush_pos_re_; float2 brush_pos_diff_re_; - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; + CurvesSurfaceTransforms transforms_; CombOperationExecutor(const bContext &C) : ctx_(C) { @@ -113,7 +115,12 @@ struct CombOperationExecutor { BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; }); - object_ = CTX_data_active_object(&C); + curves_ob_orig_ = CTX_data_active_object(&C); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); + if (curves_orig_->curves_num() == 0) { + return; + } curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); @@ -121,19 +128,12 @@ struct CombOperationExecutor { brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); - falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); - curves_id_ = static_cast<Curves *>(object_->data); - curves_ = &CurvesGeometry::wrap(curves_id_->geometry); - if (curves_->curves_num() == 0) { - return; - } + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); - point_factors_ = get_point_selection(*curves_id_); - curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + point_factors_ = get_point_selection(*curves_id_orig_); + curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_); brush_pos_prev_re_ = self_->brush_pos_last_re_; brush_pos_re_ = stroke_extension.mouse_position; @@ -162,9 +162,9 @@ struct CombOperationExecutor { this->restore_segment_lengths(changed_curves); - curves_->tag_positions_changed(); - DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + curves_orig_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); ED_region_tag_redraw(ctx_.region); } @@ -174,7 +174,7 @@ struct CombOperationExecutor { void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { this->comb_projected(r_changed_curves, brush_transform); } @@ -185,10 +185,12 @@ struct CombOperationExecutor { { const float4x4 brush_transform_inv = brush_transform.inverted(); - MutableSpan<float3> positions_cu = curves_->positions_for_write(); + MutableSpan<float3> positions_cu_orig = curves_orig_->positions_for_write(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); float4x4 projection; - ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); @@ -197,16 +199,18 @@ struct CombOperationExecutor { Vector<int> &local_changed_curves = r_changed_curves.local(); for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i]; + const float3 old_pos_cu = deformation.positions[point_i]; + const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; /* Find the position of the point in screen space. */ - float2 old_pos_re; - ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values); + float2 old_symm_pos_re; + ED_view3d_project_float_v2_m4( + ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values); const float distance_to_brush_sq_re = dist_squared_to_line_segment_v2( - old_pos_re, brush_pos_prev_re_, brush_pos_re_); + old_symm_pos_re, brush_pos_prev_re_, brush_pos_re_); if (distance_to_brush_sq_re > brush_radius_sq_re) { /* Ignore the point because it's too far away. */ continue; @@ -221,16 +225,20 @@ struct CombOperationExecutor { /* Offset the old point position in screen space and transform it back into 3D space. */ - const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; - float3 new_position_wo; + const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight; + float3 new_symm_pos_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * old_pos_cu, - new_position_re, - new_position_wo); - const float3 new_position_cu = brush_transform * - (world_to_curves_mat_ * new_position_wo); - positions_cu[point_i] = new_position_cu; + transforms_.curves_to_world * old_symm_pos_cu, + new_symm_pos_re, + new_symm_pos_wo); + const float3 new_pos_cu = brush_transform * + (transforms_.world_to_curves * new_symm_pos_wo); + + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + point_i, translation_eval); + positions_cu_orig[point_i] += translation_orig; curve_changed = true; } @@ -247,26 +255,26 @@ struct CombOperationExecutor { void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { float4x4 projection; - ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); float3 brush_start_wo, brush_end_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_prev_re_, brush_start_wo); ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_re_, brush_end_wo); - const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; - const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + const float3 brush_start_cu = transforms_.world_to_curves * brush_start_wo; + const float3 brush_end_cu = transforms_.world_to_curves * brush_end_wo; const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { this->comb_spherical(r_changed_curves, brush_transform * brush_start_cu, @@ -280,17 +288,20 @@ struct CombOperationExecutor { const float3 &brush_end_cu, const float brush_radius_cu) { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); + MutableSpan<float3> positions_cu = curves_orig_->positions_for_write(); const float brush_radius_sq_cu = pow2f(brush_radius_cu); const float3 brush_diff_cu = brush_end_cu - brush_start_cu; + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { Vector<int> &local_changed_curves = r_changed_curves.local(); for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 pos_old_cu = positions_cu[point_i]; + const float3 pos_old_cu = deformation.positions[point_i]; /* Compute distance to the brush. */ const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3( @@ -308,8 +319,12 @@ struct CombOperationExecutor { /* Combine the falloff and brush strength. */ const float weight = brush_strength_ * radius_falloff * point_factors_[point_i]; + const float3 translation_eval_cu = weight * brush_diff_cu; + const float3 translation_orig_cu = deformation.translation_from_deformed_to_original( + point_i, translation_eval_cu); + /* Update the point position. */ - positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu; + positions_cu[point_i] += translation_orig_cu; curve_changed = true; } if (curve_changed) { @@ -328,7 +343,7 @@ struct CombOperationExecutor { *ctx_.region, *ctx_.v3d, *ctx_.rv3d, - *object_, + *curves_ob_orig_, brush_pos_re_, brush_radius_base_re_); if (brush_3d.has_value()) { @@ -342,11 +357,11 @@ struct CombOperationExecutor { */ void initialize_segment_lengths() { - const Span<float3> positions_cu = curves_->positions(); - self_->segment_lengths_cu_.reinitialize(curves_->points_num()); - threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) { + const Span<float3> positions_cu = curves_orig_->positions(); + self_->segment_lengths_cu_.reinitialize(curves_orig_->points_num()); + threading::parallel_for(curves_orig_->curves_range(), 128, [&](const IndexRange range) { for (const int curve_i : range) { - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int point_i : points.drop_back(1)) { const float3 &p1_cu = positions_cu[point_i]; const float3 &p2_cu = positions_cu[point_i + 1]; @@ -363,12 +378,12 @@ struct CombOperationExecutor { void restore_segment_lengths(EnumerableThreadSpecific<Vector<int>> &changed_curves) { const Span<float> expected_lengths_cu = self_->segment_lengths_cu_; - MutableSpan<float3> positions_cu = curves_->positions_for_write(); + MutableSpan<float3> positions_cu = curves_orig_->positions_for_write(); threading::parallel_for_each(changed_curves, [&](const Vector<int> &changed_curves) { threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) { for (const int curve_i : changed_curves.as_span().slice(range)) { - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int segment_i : points.drop_back(1)) { const float3 &p1_cu = positions_cu[segment_i]; float3 &p2_cu = positions_cu[segment_i + 1]; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index 6f12d539aa2..a44499ce133 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -51,6 +51,12 @@ using blender::bke::CurvesGeometry; class DeleteOperation : public CurvesSculptStrokeOperation { private: CurvesBrush3D brush_3d_; + /** + * Need to store those in case the brush is evaluated more than once before the curves are + * evaluated again. This can happen when the mouse is moved quickly and the brush spacing is + * small. + */ + Vector<float3> deformed_positions_; friend struct DeleteOperationExecutor; @@ -76,8 +82,7 @@ struct DeleteOperationExecutor { float2 brush_pos_re_; - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; + CurvesSurfaceTransforms transforms_; DeleteOperationExecutor(const bContext &C) : ctx_(C) { @@ -101,8 +106,7 @@ struct DeleteOperationExecutor { brush_pos_re_ = stroke_extension.mouse_position; - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); @@ -111,6 +115,9 @@ struct DeleteOperationExecutor { if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { this->initialize_spherical_brush_reference_point(); } + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + self_->deformed_positions_ = deformation.positions; } Array<bool> curves_to_delete(curves_->curves_num(), false); @@ -125,12 +132,22 @@ struct DeleteOperationExecutor { } Vector<int64_t> indices; - const IndexMask mask = index_mask_ops::find_indices_based_on_predicate( + const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate( curves_->curves_range(), 4096, indices, [&](const int curve_i) { return curves_to_delete[curve_i]; }); - curves_->remove_curves(mask); + /* Remove deleted curves from the stored deformed positions. */ + const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert( + curves_->curves_range()); + Vector<float3> new_deformed_positions; + for (const IndexRange curves_range : ranges_to_keep) { + new_deformed_positions.extend( + self_->deformed_positions_.as_span().slice(curves_->points_for_curves(curves_range))); + } + self_->deformed_positions_ = std::move(new_deformed_positions); + + curves_->remove_curves(mask_to_delete); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); @@ -153,8 +170,6 @@ struct DeleteOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); - Span<float3> positions_cu = curves_->positions(); - const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); @@ -162,7 +177,7 @@ struct DeleteOperationExecutor { for (const int curve_i : curve_selection_.slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); if (points.size() == 1) { - const float3 pos_cu = brush_transform_inv * positions_cu[points.first()]; + const float3 pos_cu = brush_transform_inv * self_->deformed_positions_[points.first()]; float2 pos_re; ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); @@ -173,8 +188,8 @@ struct DeleteOperationExecutor { } for (const int segment_i : points.drop_back(1)) { - const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; - const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + const float3 pos1_cu = brush_transform_inv * self_->deformed_positions_[segment_i]; + const float3 pos2_cu = brush_transform_inv * self_->deformed_positions_[segment_i + 1]; float2 pos1_re, pos2_re; ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values); @@ -199,10 +214,10 @@ struct DeleteOperationExecutor { float3 brush_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_re_, brush_wo); - const float3 brush_cu = world_to_curves_mat_ * brush_wo; + const float3 brush_cu = transforms_.world_to_curves * brush_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); @@ -214,8 +229,6 @@ struct DeleteOperationExecutor { void delete_spherical(const float3 &brush_cu, MutableSpan<bool> curves_to_delete) { - Span<float3> positions_cu = curves_->positions(); - const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const float brush_radius_sq_cu = pow2f(brush_radius_cu); @@ -224,7 +237,7 @@ struct DeleteOperationExecutor { const IndexRange points = curves_->points_for_curve(curve_i); if (points.size() == 1) { - const float3 &pos_cu = positions_cu[points.first()]; + const float3 &pos_cu = self_->deformed_positions_[points.first()]; const float distance_sq_cu = math::distance_squared(pos_cu, brush_cu); if (distance_sq_cu < brush_radius_sq_cu) { curves_to_delete[curve_i] = true; @@ -233,8 +246,8 @@ struct DeleteOperationExecutor { } for (const int segment_i : points.drop_back(1)) { - const float3 &pos1_cu = positions_cu[segment_i]; - const float3 &pos2_cu = positions_cu[segment_i + 1]; + const float3 &pos1_cu = self_->deformed_positions_[segment_i]; + const float3 &pos2_cu = self_->deformed_positions_[segment_i + 1]; const float distance_sq_cu = dist_squared_to_line_segment_v3(brush_cu, pos1_cu, pos2_cu); if (distance_sq_cu > brush_radius_sq_cu) { diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc new file mode 100644 index 00000000000..c33ee5e0727 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc @@ -0,0 +1,922 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <numeric> + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#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_report.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "BLI_index_mask_ops.hh" +#include "BLI_kdtree.h" +#include "BLI_rand.hh" +#include "BLI_task.hh" + +#include "PIL_time.h" + +#include "GEO_add_curves_on_mesh.hh" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" + +#include "WM_api.h" + +#include "curves_sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { + +class DensityAddOperation : public CurvesSculptStrokeOperation { + private: + /** Used when some data should be interpolated from existing curves. */ + KDTree_3d *original_curve_roots_kdtree_ = nullptr; + /** Contains curve roots of all curves that existed before the brush started. */ + KDTree_3d *deformed_curve_roots_kdtree_ = nullptr; + /** Root positions of curves that have been added in the current brush stroke. */ + Vector<float3> new_deformed_root_positions_; + int original_curve_num_ = 0; + + friend struct DensityAddOperationExecutor; + + public: + ~DensityAddOperation() override + { + if (original_curve_roots_kdtree_ != nullptr) { + BLI_kdtree_3d_free(original_curve_roots_kdtree_); + } + if (deformed_curve_roots_kdtree_ != nullptr) { + BLI_kdtree_3d_free(deformed_curve_roots_kdtree_); + } + } + + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +struct DensityAddOperationExecutor { + DensityAddOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; + + Object *surface_ob_orig_ = nullptr; + Mesh *surface_orig_ = nullptr; + + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + Span<MLoopTri> surface_looptris_eval_; + VArraySpan<float2> surface_uv_map_eval_; + BVHTreeFromMesh surface_bvh_eval_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + const BrushCurvesSculptSettings *brush_settings_ = nullptr; + + float brush_strength_; + float brush_radius_re_; + float2 brush_pos_re_; + + CurvesSurfaceTransforms transforms_; + + DensityAddOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(DensityAddOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) + { + self_ = &self; + curves_ob_orig_ = CTX_data_active_object(&C); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); + + if (stroke_extension.is_first) { + self_->original_curve_num_ = curves_orig_->curves_num(); + } + + if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) { + report_missing_surface(stroke_extension.reports); + return; + } + + surface_ob_orig_ = curves_id_orig_->surface; + surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data); + if (surface_orig_->totpoly == 0) { + report_empty_original_surface(stroke_extension.reports); + return; + } + + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); + if (surface_eval_->totpoly == 0) { + report_empty_evaluated_surface(stroke_extension.reports); + return; + } + + 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_)}; + /* Find UV map. */ + VArraySpan<float2> surface_uv_map; + if (curves_id_orig_->surface_uv_map != nullptr) { + surface_uv_map = surface_orig_->attributes() + .lookup<float2>(curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); + surface_uv_map_eval_ = surface_eval_->attributes() + .lookup<float2>(curves_id_orig_->surface_uv_map, + ATTR_DOMAIN_CORNER); + } + if (surface_uv_map.is_empty()) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (surface_uv_map_eval_.is_empty()) { + report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); + return; + } + + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_settings_ = brush_->curves_sculpt_settings; + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + brush_radius_re_ = brush_radius_get(*ctx_.scene, *brush_, stroke_extension); + brush_pos_re_ = stroke_extension.mouse_position; + + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + + Vector<float3> new_positions_cu; + Vector<float2> new_uvs; + const double time = PIL_check_seconds_timer() * 1000000.0; + RandomNumberGenerator rng{*(uint32_t *)(&time)}; + + /* Find potential new curve root points. */ + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->sample_projected_with_symmetry(rng, new_uvs, new_positions_cu); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->sample_spherical_with_symmetry(rng, new_uvs, new_positions_cu); + } + else { + BLI_assert_unreachable(); + } + for (float3 &pos : new_positions_cu) { + pos = transforms_.surface_to_curves * pos; + } + + if (stroke_extension.is_first) { + this->prepare_curve_roots_kdtrees(); + } + + const int already_added_curves = self_->new_deformed_root_positions_.size(); + KDTree_3d *new_roots_kdtree = BLI_kdtree_3d_new(already_added_curves + + new_positions_cu.size()); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(new_roots_kdtree); }); + + /* Used to tag all curves that are too close to existing curves or too close to other new + * curves. */ + Array<bool> new_curve_skipped(new_positions_cu.size(), false); + threading::parallel_invoke( + 512 < already_added_curves + new_positions_cu.size(), + /* Build kdtree from root points created by the current stroke. */ + [&]() { + for (const int i : IndexRange(already_added_curves)) { + BLI_kdtree_3d_insert(new_roots_kdtree, -1, self_->new_deformed_root_positions_[i]); + } + for (const int new_i : new_positions_cu.index_range()) { + const float3 &root_pos_cu = new_positions_cu[new_i]; + BLI_kdtree_3d_insert(new_roots_kdtree, new_i, root_pos_cu); + } + BLI_kdtree_3d_balance(new_roots_kdtree); + }, + /* Check which new root points are close to roots that existed before the current stroke + * started. */ + [&]() { + threading::parallel_for( + new_positions_cu.index_range(), 128, [&](const IndexRange range) { + for (const int new_i : range) { + const float3 &new_root_pos_cu = new_positions_cu[new_i]; + KDTreeNearest_3d nearest; + nearest.dist = FLT_MAX; + BLI_kdtree_3d_find_nearest( + self_->deformed_curve_roots_kdtree_, new_root_pos_cu, &nearest); + if (nearest.dist < brush_settings_->minimum_distance) { + new_curve_skipped[new_i] = true; + } + } + }); + }); + + /* Find new points that are too close too other new points. */ + for (const int new_i : new_positions_cu.index_range()) { + if (new_curve_skipped[new_i]) { + continue; + } + const float3 &root_pos_cu = new_positions_cu[new_i]; + BLI_kdtree_3d_range_search_cb_cpp( + new_roots_kdtree, + root_pos_cu, + brush_settings_->minimum_distance, + [&](const int other_new_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + if (other_new_i == -1) { + new_curve_skipped[new_i] = true; + return false; + } + if (new_i == other_new_i) { + return true; + } + new_curve_skipped[other_new_i] = true; + return true; + }); + } + + /* Remove points that are too close to others. */ + for (int64_t i = new_positions_cu.size() - 1; i >= 0; i--) { + if (new_curve_skipped[i]) { + new_positions_cu.remove_and_reorder(i); + new_uvs.remove_and_reorder(i); + } + } + self_->new_deformed_root_positions_.extend(new_positions_cu); + + /* Find normals. */ + if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_orig_); + } + const Span<float3> corner_normals_su = { + 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 geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig}; + + geometry::AddCurvesOnMeshInputs add_inputs; + add_inputs.uvs = new_uvs; + add_inputs.interpolate_length = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; + add_inputs.interpolate_shape = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; + add_inputs.interpolate_point_count = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; + add_inputs.fallback_curve_length = brush_settings_->curve_length; + add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve); + add_inputs.transforms = &transforms_; + add_inputs.surface = surface_orig_; + add_inputs.corner_normals_su = corner_normals_su; + add_inputs.reverse_uv_sampler = &reverse_uv_sampler; + add_inputs.old_roots_kdtree = self_->original_curve_roots_kdtree_; + + const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh( + *curves_orig_, add_inputs); + + if (add_outputs.uv_error) { + report_invalid_uv_map(stroke_extension.reports); + } + + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); + ED_region_tag_redraw(ctx_.region); + } + + void prepare_curve_roots_kdtrees() + { + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); + const Span<int> curve_offsets = curves_orig_->offsets(); + const Span<float3> original_positions = curves_orig_->positions(); + const Span<float3> deformed_positions = deformation.positions; + BLI_assert(original_positions.size() == deformed_positions.size()); + + auto roots_kdtree_from_positions = [&](const Span<float3> positions) { + KDTree_3d *kdtree = BLI_kdtree_3d_new(curves_orig_->curves_num()); + for (const int curve_i : curves_orig_->curves_range()) { + const int root_point_i = curve_offsets[curve_i]; + BLI_kdtree_3d_insert(kdtree, curve_i, positions[root_point_i]); + } + BLI_kdtree_3d_balance(kdtree); + return kdtree; + }; + + threading::parallel_invoke( + 1024 < original_positions.size() + deformed_positions.size(), + [&]() { + self_->original_curve_roots_kdtree_ = roots_kdtree_from_positions(original_positions); + }, + [&]() { + self_->deformed_curve_roots_kdtree_ = roots_kdtree_from_positions(deformed_positions); + }); + } + + void sample_projected_with_symmetry(RandomNumberGenerator &rng, + Vector<float2> &r_uvs, + Vector<float3> &r_positions_su) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_orig_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + const float4x4 brush_transform_inv = brush_transform.inverted(); + const float4x4 transform = transforms_.curves_to_surface * brush_transform * + transforms_.world_to_curves; + Vector<float3> positions_su; + Vector<float3> bary_coords; + Vector<int> looptri_indices; + const int new_points = bke::mesh_surface_sample::sample_surface_points_projected( + rng, + *surface_eval_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re_, + [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) { + float3 start_wo, end_wo; + ED_view3d_win_to_segment_clipped( + ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true); + r_start_su = transform * start_wo; + r_end_su = transform * end_wo; + }, + true, + brush_settings_->density_add_attempts, + brush_settings_->density_add_attempts, + bary_coords, + looptri_indices, + positions_su); + + /* Remove some sampled points randomly based on the brush falloff and strength. */ + for (int i = new_points - 1; i >= 0; i--) { + const float3 pos_su = positions_su[i]; + const float3 pos_cu = brush_transform_inv * transforms_.surface_to_curves * pos_su; + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + const float dist_to_brush_re = math::distance(brush_pos_re_, pos_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re_); + const float weight = brush_strength_ * radius_falloff; + if (rng.get_float() > weight) { + bary_coords.remove_and_reorder(i); + looptri_indices.remove_and_reorder(i); + positions_su.remove_and_reorder(i); + } + } + + for (const int i : bary_coords.index_range()) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_uvs.append(uv); + } + r_positions_su.extend(positions_su); + } + } + + void sample_spherical_with_symmetry(RandomNumberGenerator &rng, + Vector<float2> &r_uvs, + Vector<float3> &r_positions_su) + { + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + transforms_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re_); + if (!brush_3d.has_value()) { + return; + } + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_orig_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + const float3 brush_pos_cu = brush_transform * brush_3d->position_cu; + const float3 brush_pos_su = transforms_.curves_to_surface * brush_pos_cu; + const float brush_radius_su = transform_brush_radius( + transforms_.curves_to_surface, brush_pos_cu, brush_3d->radius_cu); + const float brush_radius_sq_su = pow2f(brush_radius_su); + + Vector<int> selected_looptri_indices; + BLI_bvhtree_range_query_cpp( + *surface_bvh_eval_.tree, + brush_pos_su, + brush_radius_su, + [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { + selected_looptri_indices.append(index); + }); + + const float brush_plane_area_su = M_PI * brush_radius_sq_su; + const float approximate_density_su = brush_settings_->density_add_attempts / + brush_plane_area_su; + + Vector<float3> positions_su; + Vector<float3> bary_coords; + Vector<int> looptri_indices; + const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical( + rng, + *surface_eval_, + selected_looptri_indices, + brush_pos_su, + brush_radius_su, + approximate_density_su, + bary_coords, + looptri_indices, + positions_su); + + /* Remove some sampled points randomly based on the brush falloff and strength. */ + for (int i = new_points - 1; i >= 0; i--) { + const float3 pos_su = positions_su[i]; + const float3 pos_cu = transforms_.surface_to_curves * pos_su; + const float dist_to_brush_cu = math::distance(pos_cu, brush_pos_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_3d->radius_cu); + const float weight = brush_strength_ * radius_falloff; + if (rng.get_float() > weight) { + bary_coords.remove_and_reorder(i); + looptri_indices.remove_and_reorder(i); + positions_su.remove_and_reorder(i); + } + } + + for (const int i : bary_coords.index_range()) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_uvs.append(uv); + } + r_positions_su.extend(positions_su); + } + } +}; + +void DensityAddOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) +{ + DensityAddOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +class DensitySubtractOperation : public CurvesSculptStrokeOperation { + private: + friend struct DensitySubtractOperationExecutor; + + /** + * Deformed root positions of curves that still exist. This has to be stored in case the brush is + * executed more than once before the curves are evaluated again. This can happen when the mouse + * is moved quickly and the brush spacing is small. + */ + Vector<float3> deformed_root_positions_; + + public: + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct DensitySubtractOperationExecutor { + DensitySubtractOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + Object *surface_ob_orig_ = nullptr; + Mesh *surface_orig_ = nullptr; + + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + BVHTreeFromMesh surface_bvh_eval_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + float2 brush_pos_re_; + + float minimum_distance_; + + CurvesSurfaceTransforms transforms_; + + KDTree_3d *root_points_kdtree_; + + DensitySubtractOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(DensitySubtractOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) + { + self_ = &self; + + object_ = CTX_data_active_object(&C); + + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } + + surface_ob_orig_ = curves_id_->surface; + if (surface_ob_orig_ == nullptr) { + return; + } + surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data); + + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); + + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + brush_pos_re_ = stroke_extension.mouse_position; + + minimum_distance_ = brush_->curves_sculpt_settings->minimum_distance; + + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + + if (stroke_extension.is_first) { + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + for (const int curve_i : curves_->curves_range()) { + const int first_point_i = curves_->offsets()[curve_i]; + self_->deformed_root_positions_.append(deformation.positions[first_point_i]); + } + } + + root_points_kdtree_ = BLI_kdtree_3d_new(curve_selection_.size()); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(root_points_kdtree_); }); + for (const int curve_i : curve_selection_) { + const float3 &pos_cu = self_->deformed_root_positions_[curve_i]; + BLI_kdtree_3d_insert(root_points_kdtree_, curve_i, pos_cu); + } + BLI_kdtree_3d_balance(root_points_kdtree_); + + /* Find all curves that should be deleted. */ + Array<bool> curves_to_delete(curves_->curves_num(), false); + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->reduce_density_projected_with_symmetry(curves_to_delete); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->reduce_density_spherical_with_symmetry(curves_to_delete); + } + else { + BLI_assert_unreachable(); + } + + Vector<int64_t> indices; + const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate( + curves_->curves_range(), 4096, indices, [&](const int curve_i) { + return curves_to_delete[curve_i]; + }); + + /* Remove deleted curves from the stored deformed root positions. */ + const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert( + curves_->curves_range()); + BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size()); + Vector<float3> new_deformed_positions; + for (const IndexRange range : ranges_to_keep) { + new_deformed_positions.extend(self_->deformed_root_positions_.as_span().slice(range)); + } + self_->deformed_root_positions_ = std::move(new_deformed_positions); + + curves_->remove_curves(mask_to_delete); + BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size()); + + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); + } + + void reduce_density_projected_with_symmetry(MutableSpan<bool> curves_to_delete) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->reduce_density_projected(brush_transform, curves_to_delete); + } + } + + void reduce_density_projected(const float4x4 &brush_transform, + MutableSpan<bool> curves_to_delete) + { + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + /* Randomly select the curves that are allowed to be removed, based on the brush radius and + * 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)); + + for (const int curve_i : range) { + if (curves_to_delete[curve_i]) { + allow_remove_curve[curve_i] = true; + continue; + } + const float3 pos_cu = brush_transform * self_->deformed_root_positions_[curve_i]; + + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re); + const float weight = brush_strength_ * radius_falloff; + if (rng.get_float() < weight) { + allow_remove_curve[curve_i] = true; + } + } + }); + + /* Detect curves that are too close to other existing curves. */ + for (const int curve_i : curve_selection_) { + if (curves_to_delete[curve_i]) { + continue; + } + if (!allow_remove_curve[curve_i]) { + continue; + } + const float3 orig_pos_cu = self_->deformed_root_positions_[curve_i]; + const float3 pos_cu = brush_transform * orig_pos_cu; + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + BLI_kdtree_3d_range_search_cb_cpp( + root_points_kdtree_, + orig_pos_cu, + minimum_distance_, + [&](const int other_curve_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + if (other_curve_i == curve_i) { + return true; + } + if (allow_remove_curve[other_curve_i]) { + curves_to_delete[other_curve_i] = true; + } + return true; + }); + } + } + + void reduce_density_spherical_with_symmetry(MutableSpan<bool> curves_to_delete) + { + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + transforms_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re); + if (!brush_3d.has_value()) { + return; + } + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + const float3 brush_pos_cu = brush_transform * brush_3d->position_cu; + this->reduce_density_spherical(brush_pos_cu, brush_3d->radius_cu, curves_to_delete); + } + } + + void reduce_density_spherical(const float3 &brush_pos_cu, + const float brush_radius_cu, + MutableSpan<bool> curves_to_delete) + { + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + /* Randomly select the curves that are allowed to be removed, based on the brush radius and + * 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)); + + for (const int curve_i : range) { + if (curves_to_delete[curve_i]) { + allow_remove_curve[curve_i] = true; + continue; + } + const float3 pos_cu = self_->deformed_root_positions_[curve_i]; + + const float dist_to_brush_sq_cu = math::distance_squared(brush_pos_cu, pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + const float weight = brush_strength_ * radius_falloff; + if (rng.get_float() < weight) { + allow_remove_curve[curve_i] = true; + } + } + }); + + /* Detect curves that are too close to other existing curves. */ + for (const int curve_i : curve_selection_) { + if (curves_to_delete[curve_i]) { + continue; + } + if (!allow_remove_curve[curve_i]) { + continue; + } + const float3 &pos_cu = self_->deformed_root_positions_[curve_i]; + const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + BLI_kdtree_3d_range_search_cb_cpp( + root_points_kdtree_, + pos_cu, + minimum_distance_, + [&](const int other_curve_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + if (other_curve_i == curve_i) { + return true; + } + if (allow_remove_curve[other_curve_i]) { + curves_to_delete[other_curve_i] = true; + } + return true; + }); + } + } +}; + +void DensitySubtractOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) +{ + DensitySubtractOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +/** + * Detects whether the brush should be in Add or Subtract mode. + */ +static bool use_add_density_mode(const BrushStrokeMode brush_mode, + const bContext &C, + const StrokeExtension &stroke_start) +{ + const Scene &scene = *CTX_data_scene(&C); + const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint); + const Depsgraph &depsgraph = *CTX_data_depsgraph_on_load(&C); + const ARegion ®ion = *CTX_wm_region(&C); + const View3D &v3d = *CTX_wm_view3d(&C); + + const eBrushCurvesSculptDensityMode density_mode = static_cast<eBrushCurvesSculptDensityMode>( + brush.curves_sculpt_settings->density_mode); + const bool use_invert = brush_mode == BRUSH_STROKE_INVERT; + + if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_ADD) { + return !use_invert; + } + if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_REMOVE) { + return use_invert; + } + + const Object &curves_ob_orig = *CTX_data_active_object(&C); + const Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data); + Object *surface_ob_orig = curves_id_orig.surface; + if (surface_ob_orig == nullptr) { + return true; + } + Object *surface_ob_eval = DEG_get_evaluated_object(&depsgraph, surface_ob_orig); + if (surface_ob_eval == nullptr) { + return true; + } + const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id_orig.geometry); + if (curves.curves_num() <= 1) { + return true; + } + const Mesh *surface_mesh_eval = BKE_object_get_evaluated_mesh(surface_ob_eval); + if (surface_mesh_eval == nullptr) { + return true; + } + + const CurvesSurfaceTransforms transforms(curves_ob_orig, curves_id_orig.surface); + BVHTreeFromMesh surface_bvh_eval; + BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_mesh_eval, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); }); + + const float2 brush_pos_re = stroke_start.mouse_position; + /* Reduce radius so that only an inner circle is used to determine the existing density. */ + const float brush_radius_re = BKE_brush_size_get(&scene, &brush) * 0.5f; + + /* Find the surface point under the brush. */ + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush( + depsgraph, region, v3d, transforms, surface_bvh_eval, brush_pos_re, brush_radius_re); + if (!brush_3d.has_value()) { + return true; + } + + const float3 brush_pos_cu = brush_3d->position_cu; + const float brush_radius_cu = brush_3d->radius_cu; + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_ob_orig); + const Span<int> offsets = curves.offsets(); + + /* Compute distance from brush to curve roots. */ + Array<std::pair<float, int>> distances_sq_to_brush(curves.curves_num()); + threading::EnumerableThreadSpecific<int> valid_curve_count_by_thread; + threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) { + int &valid_curve_count = valid_curve_count_by_thread.local(); + for (const int curve_i : range) { + const int root_point_i = offsets[curve_i]; + const float3 &root_pos_cu = deformation.positions[root_point_i]; + const float dist_sq_cu = math::distance_squared(root_pos_cu, brush_pos_cu); + if (dist_sq_cu < brush_radius_sq_cu) { + distances_sq_to_brush[curve_i] = {math::distance_squared(root_pos_cu, brush_pos_cu), + curve_i}; + valid_curve_count++; + } + else { + distances_sq_to_brush[curve_i] = {FLT_MAX, -1}; + } + } + }); + const int valid_curve_count = std::accumulate( + valid_curve_count_by_thread.begin(), valid_curve_count_by_thread.end(), 0); + + /* Find a couple of curves that are closest to the brush center. */ + const int check_curve_count = std::min<int>(8, valid_curve_count); + std::partial_sort(distances_sq_to_brush.begin(), + distances_sq_to_brush.begin() + check_curve_count, + distances_sq_to_brush.end()); + + /* Compute the minimum pair-wise distance between the curve roots that are close to the brush + * center. */ + float min_dist_sq_cu = FLT_MAX; + for (const int i : IndexRange(check_curve_count)) { + const float3 &pos_i = deformation.positions[offsets[distances_sq_to_brush[i].second]]; + for (int j = i + 1; j < check_curve_count; j++) { + const float3 &pos_j = deformation.positions[offsets[distances_sq_to_brush[j].second]]; + const float dist_sq_cu = math::distance_squared(pos_i, pos_j); + math::min_inplace(min_dist_sq_cu, dist_sq_cu); + } + } + + const float min_dist_cu = std::sqrt(min_dist_sq_cu); + if (min_dist_cu > brush.curves_sculpt_settings->minimum_distance) { + return true; + } + + return false; +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_density_operation( + const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start) +{ + if (use_add_density_mode(brush_mode, C, stroke_start)) { + return std::make_unique<DensityAddOperation>(); + } + return std::make_unique<DensitySubtractOperation>(); +} + +} // namespace blender::ed::sculpt_paint 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 3420659520b..0ca22004540 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -4,8 +4,7 @@ #include "BLI_enumerable_thread_specific.hh" #include "BLI_float4x4.hh" -#include "BLI_kdtree.h" -#include "BLI_rand.hh" +#include "BLI_length_parameterize.hh" #include "BLI_vector.hh" #include "PIL_time.h" @@ -14,19 +13,13 @@ #include "BKE_attribute_math.hh" #include "BKE_brush.h" -#include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_paint.h" -#include "BKE_spline.hh" #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" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -70,6 +63,24 @@ class ShrinkCurvesEffect : public CurvesEffect { private: const Brush &brush_; + /** Storage of per-curve parameterization data to avoid reallocation. */ + struct ParameterizationBuffers { + Array<float3> old_positions; + Array<float> old_lengths; + Array<float> sample_lengths; + Array<int> indices; + Array<float> factors; + + void reinitialize(const int points_num) + { + this->old_positions.reinitialize(points_num); + this->old_lengths.reinitialize(length_parameterize::segments_num(points_num, false)); + this->sample_lengths.reinitialize(points_num); + this->indices.reinitialize(points_num); + this->factors.reinitialize(points_num); + } + }; + public: ShrinkCurvesEffect(const Brush &brush) : brush_(brush) { @@ -81,46 +92,42 @@ class ShrinkCurvesEffect : public CurvesEffect { { MutableSpan<float3> positions_cu = curves.positions_for_write(); threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { + ParameterizationBuffers data; 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 curve_points = curves.points_for_curve(curve_i); - this->shrink_curve(positions_cu, curve_points, move_distance_cu); + const IndexRange points = curves.points_for_curve(curve_i); + this->shrink_curve(positions_cu.slice(points), move_distance_cu, data); } }); } + private: void shrink_curve(MutableSpan<float3> positions, - const IndexRange curve_points, - const float shrink_length) const + const float shrink_length, + ParameterizationBuffers &data) const { - PolySpline spline; - spline.resize(curve_points.size()); - MutableSpan<float3> spline_positions = spline.positions(); - spline_positions.copy_from(positions.slice(curve_points)); - spline.mark_cache_invalid(); + namespace lp = length_parameterize; + data.reinitialize(positions.size()); + + /* Copy the old positions to facilitate mixing from neighbors for the resulting curve. */ + data.old_positions.as_mutable_span().copy_from(positions); + + lp::accumulate_lengths<float3>(data.old_positions, false, data.old_lengths); + const float min_length = brush_.curves_sculpt_settings->minimum_length; - const float old_length = spline.length(); + const float old_length = data.old_lengths.last(); const float new_length = std::max(min_length, old_length - shrink_length); const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f); - Vector<float> old_point_lengths; - old_point_lengths.append(0.0f); - for (const int i : spline_positions.index_range().drop_back(1)) { - const float3 &p1 = spline_positions[i]; - const float3 &p2 = spline_positions[i + 1]; - const float length = math::distance(p1, p2); - old_point_lengths.append(old_point_lengths.last() + length); + data.sample_lengths.first() = 0.0f; + for (const int i : data.old_lengths.index_range()) { + data.sample_lengths[i + 1] = data.old_lengths[i] * length_factor; } - for (const int i : spline_positions.index_range()) { - const float eval_length = old_point_lengths[i] * length_factor; - const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1}); - positions[curve_points[i]] = p; - } + lp::sample_at_lengths(data.old_lengths, data.sample_lengths, data.indices, data.factors); + + lp::interpolate<float3>(data.old_positions, data.indices, data.factors, positions); } }; @@ -137,21 +144,20 @@ class ExtrapolateCurvesEffect : public CurvesEffect { 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 curve_points = curves.points_for_curve(curve_i); + const IndexRange points = curves.points_for_curve(curve_i); - if (curve_points.size() <= 1) { + if (points.size() <= 1) { continue; } - const float3 old_last_pos_cu = positions_cu[curve_points.last()]; + const float3 old_last_pos_cu = positions_cu[points.last()]; /* Use some point within the curve rather than the end point to smooth out some random * variation. */ - const float3 direction_reference_point = - positions_cu[curve_points[curve_points.size() / 2]]; + const float3 direction_reference_point = positions_cu[points[points.size() / 2]]; 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(curve_points), new_last_pos_cu); + move_last_point_and_resample(positions_cu.slice(points), new_last_pos_cu); } }); } @@ -247,8 +253,7 @@ struct CurvesEffectOperationExecutor { eBrushFalloffShape falloff_shape_; - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; + CurvesSurfaceTransforms transforms_; float2 brush_pos_start_re_; float2 brush_pos_end_re_; @@ -290,8 +295,7 @@ struct CurvesEffectOperationExecutor { falloff_shape_ = eBrushFalloffShape(brush_->falloff_shape); - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); brush_pos_start_re_ = self.last_mouse_position_; brush_pos_end_re_ = stroke_extension.mouse_position; @@ -337,7 +341,8 @@ struct CurvesEffectOperationExecutor { void gather_influences_projected( threading::EnumerableThreadSpecific<Influences> &influences_for_thread) { - const Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); @@ -345,7 +350,7 @@ struct CurvesEffectOperationExecutor { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); Vector<float4x4> symmetry_brush_transforms_inv; - for (const float4x4 brush_transform : symmetry_brush_transforms) { + for (const float4x4 &brush_transform : symmetry_brush_transforms) { symmetry_brush_transforms_inv.append(brush_transform.inverted()); } @@ -363,8 +368,8 @@ struct CurvesEffectOperationExecutor { float max_move_distance_cu = 0.0f; for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) { for (const int segment_i : points.drop_back(1)) { - const float3 p1_cu = brush_transform_inv * positions_cu[segment_i]; - const float3 p2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + const float3 p1_cu = brush_transform_inv * deformation.positions[segment_i]; + const float3 p2_cu = brush_transform_inv * deformation.positions[segment_i + 1]; float2 p1_re, p2_re; ED_view3d_project_float_v2_m4(ctx_.region, p1_cu, p1_re, projection.values); @@ -398,16 +403,16 @@ struct CurvesEffectOperationExecutor { float3 brush_start_pos_wo, brush_end_pos_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * closest_on_segment_cu, + transforms_.curves_to_world * closest_on_segment_cu, brush_pos_start_re_, brush_start_pos_wo); ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * closest_on_segment_cu, + transforms_.curves_to_world * closest_on_segment_cu, brush_pos_end_re_, brush_end_pos_wo); - const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo; - const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo; + const float3 brush_start_pos_cu = transforms_.world_to_curves * brush_start_pos_wo; + const float3 brush_end_pos_cu = transforms_.world_to_curves * brush_end_pos_wo; const float move_distance_cu = weight * math::distance(brush_start_pos_cu, brush_end_pos_cu); @@ -425,21 +430,22 @@ struct CurvesEffectOperationExecutor { void gather_influences_spherical( threading::EnumerableThreadSpecific<Influences> &influences_for_thread) { - const Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); float3 brush_pos_start_wo, brush_pos_end_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_start_re_, brush_pos_start_wo); ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_end_re_, brush_pos_end_wo); - const float3 brush_pos_start_cu = world_to_curves_mat_ * brush_pos_start_wo; - const float3 brush_pos_end_cu = world_to_curves_mat_ * brush_pos_end_wo; + const float3 brush_pos_start_cu = transforms_.world_to_curves * brush_pos_start_wo; + const float3 brush_pos_end_cu = transforms_.world_to_curves * brush_pos_end_wo; const float3 brush_pos_diff_cu = brush_pos_end_cu - brush_pos_start_cu; const float brush_pos_diff_length_cu = math::length(brush_pos_diff_cu); const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; @@ -463,8 +469,8 @@ struct CurvesEffectOperationExecutor { const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu; for (const int segment_i : points.drop_back(1)) { - const float3 &p1_cu = positions_cu[segment_i]; - const float3 &p2_cu = positions_cu[segment_i + 1]; + const float3 &p1_cu = deformation.positions[segment_i]; + const float3 &p2_cu = deformation.positions[segment_i + 1]; float3 closest_on_segment_cu; float3 closest_on_brush_cu; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index 5c926b1a740..5c8c0cedc6f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -12,8 +12,11 @@ #include "BLI_virtual_array.hh" #include "BKE_attribute.h" +#include "BKE_crazyspace.hh" #include "BKE_curves.hh" +#include "ED_curves_sculpt.h" + struct ARegion; struct RegionView3D; struct Depsgraph; @@ -21,15 +24,19 @@ struct View3D; struct Object; struct Brush; struct Scene; +struct BVHTreeFromMesh; +struct ReportList; namespace blender::ed::sculpt_paint { using bke::CurvesGeometry; +using bke::CurvesSurfaceTransforms; struct StrokeExtension { bool is_first; float2 mouse_position; float pressure; + ReportList *reports = nullptr; }; float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension); @@ -51,8 +58,7 @@ class CurvesSculptStrokeOperation { virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0; }; -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, - ReportList *reports); +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation(); @@ -60,6 +66,13 @@ std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation( const BrushStrokeMode brush_mode, const bContext &C); std::unique_ptr<CurvesSculptStrokeOperation> new_selection_paint_operation( const BrushStrokeMode brush_mode, const bContext &C); +std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStrokeMode brush_mode, + const bContext &C); +std::unique_ptr<CurvesSculptStrokeOperation> new_smooth_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_puff_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_density_operation( + const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start); +std::unique_ptr<CurvesSculptStrokeOperation> new_slide_operation(); struct CurvesBrush3D { float3 position_cu; @@ -89,31 +102,37 @@ VArray<float> get_curves_selection(const Curves &curves_id); */ VArray<float> get_point_selection(const Curves &curves_id); -/** - * Find curves that have any point selected (a selection factor greater than zero), - * or curves that have their own selection factor greater than zero. - */ -IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices); - void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position); -float3 compute_surface_point_normal(const MLoopTri &looptri, - const float3 &bary_coord, - const Span<float3> corner_normals); - -float3 compute_bary_coord_in_triangle(const Mesh &mesh, - const MLoopTri &looptri, - const float3 &position); - class CurvesSculptCommonContext { public: const Depsgraph *depsgraph = nullptr; const Scene *scene = nullptr; ARegion *region = nullptr; const View3D *v3d = nullptr; - const RegionView3D *rv3d = nullptr; + RegionView3D *rv3d = nullptr; CurvesSculptCommonContext(const bContext &C); }; +std::optional<CurvesBrush3D> sample_curves_surface_3d_brush( + const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const CurvesSurfaceTransforms &transforms, + const BVHTreeFromMesh &surface_bvh, + const float2 &brush_pos_re, + const float brush_radius_re); + +float transform_brush_radius(const float4x4 &transform, + const float3 &brush_position, + const float old_radius); + +void report_empty_original_surface(ReportList *reports); +void report_empty_evaluated_surface(ReportList *reports); +void report_missing_surface(ReportList *reports); +void report_missing_uv_map_on_original_surface(ReportList *reports); +void report_missing_uv_map_on_evaluated_surface(ReportList *reports); +void report_invalid_uv_map(ReportList *reports); + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 5d6ffa67005..6e1ac24e21b 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -1,49 +1,53 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_kdtree.h" +#include "BLI_rand.hh" #include "BLI_utildefines.h" +#include "BLI_vector_set.hh" -#include "BKE_attribute_math.hh" #include "BKE_brush.h" #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" -#include "BKE_geometry_set.hh" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" +#include "BKE_modifier.h" +#include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_spline.hh" #include "WM_api.h" +#include "WM_message.h" #include "WM_toolsystem.h" +#include "ED_curves.h" #include "ED_curves_sculpt.h" #include "ED_image.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_space_api.h" #include "ED_view3d.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "DNA_brush_types.h" #include "DNA_curves_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_screen_types.h" #include "RNA_access.h" - -#include "BLI_index_mask_ops.hh" -#include "BLI_kdtree.h" -#include "BLI_math_vector.hh" -#include "BLI_rand.hh" - -#include "PIL_time.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" #include "curves_sculpt_intern.h" #include "curves_sculpt_intern.hh" #include "paint_intern.h" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + /* -------------------------------------------------------------------- */ /** \name Poll Functions * \{ */ @@ -105,8 +109,8 @@ float brush_strength_get(const Scene &scene, return BKE_brush_alpha_get(&scene, &brush) * brush_strength_factor(brush, stroke_extension); } -static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext &C, - wmOperator &op) +static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation( + bContext &C, wmOperator &op, const StrokeExtension &stroke_start) { const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode")); @@ -121,11 +125,21 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte case CURVES_SCULPT_TOOL_SNAKE_HOOK: return new_snake_hook_operation(); case CURVES_SCULPT_TOOL_ADD: - return new_add_operation(C, op.reports); + return new_add_operation(); case CURVES_SCULPT_TOOL_GROW_SHRINK: return new_grow_shrink_operation(mode, C); case CURVES_SCULPT_TOOL_SELECTION_PAINT: return new_selection_paint_operation(mode, C); + case CURVES_SCULPT_TOOL_PINCH: + return new_pinch_operation(mode, C); + case CURVES_SCULPT_TOOL_SMOOTH: + return new_smooth_operation(); + case CURVES_SCULPT_TOOL_PUFF: + return new_puff_operation(); + case CURVES_SCULPT_TOOL_DENSITY: + return new_density_operation(mode, C, stroke_start); + case CURVES_SCULPT_TOOL_SLIDE: + return new_slide_operation(); } BLI_assert_unreachable(); return {}; @@ -136,7 +150,10 @@ struct SculptCurvesBrushStrokeData { PaintStroke *stroke; }; -static bool stroke_get_location(bContext *C, float out[3], const float mouse[2]) +static bool stroke_get_location(bContext *C, + float out[3], + const float mouse[2], + bool UNUSED(force_original)) { out[0] = mouse[0]; out[1] = mouse[1]; @@ -162,10 +179,11 @@ static void stroke_update_step(bContext *C, StrokeExtension stroke_extension; RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position); stroke_extension.pressure = RNA_float_get(stroke_element, "pressure"); + stroke_extension.reports = op->reports; if (!op_data->operation) { stroke_extension.is_first = true; - op_data->operation = start_brush_operation(*C, *op); + op_data->operation = start_brush_operation(*C, *op, stroke_extension); } else { stroke_extension.is_first = false; @@ -251,21 +269,11 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot) /** \name * CURVES_OT_sculptmode_toggle * \{ */ -static bool curves_sculptmode_toggle_poll(bContext *C) -{ - const Object *ob = CTX_data_active_object(C); - if (ob == nullptr) { - return false; - } - if (ob->type != OB_CURVES) { - return false; - } - return true; -} - static void curves_sculptmode_enter(bContext *C) { Scene *scene = CTX_data_scene(C); + wmMsgBus *mbus = CTX_wm_message_bus(C); + Object *ob = CTX_data_active_object(C); BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt; @@ -274,8 +282,9 @@ static void curves_sculptmode_enter(bContext *C) ED_paint_cursor_start(&curves_sculpt->paint, CURVES_SCULPT_mode_poll_view3d); - /* Update for mode change. */ + /* Necessary to change the object mode on the evaluated object. */ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr); } @@ -288,6 +297,8 @@ static void curves_sculptmode_exit(bContext *C) static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); + wmMsgBus *mbus = CTX_wm_message_bus(C); + const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES; if (is_mode_set) { @@ -304,6 +315,10 @@ static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op) } WM_toolsystem_update_from_context_view3d(C); + + /* Necessary to change the object mode on the evaluated object. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr); return OPERATOR_FINISHED; } @@ -315,13 +330,937 @@ static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot) ot->description = "Enter/Exit sculpt mode for curves"; ot->exec = curves_sculptmode_toggle_exec; - ot->poll = curves_sculptmode_toggle_poll; + ot->poll = curves::curves_poll; ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; } /** \} */ +namespace select_random { + +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)}; + + const bool partial = RNA_boolean_get(op->ptr, "partial"); + const bool constant_per_curve = RNA_boolean_get(op->ptr, "constant_per_curve"); + const float probability = RNA_float_get(op->ptr, "probability"); + const float min_value = RNA_float_get(op->ptr, "min"); + const auto next_partial_random_value = [&]() { + return rng.get_float() * (1.0f - min_value) + min_value; + }; + const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; }; + + for (Curves *curves_id : unique_curves) { + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + const bool was_anything_selected = curves::has_anything_selected(*curves_id); + switch (curves_id->selection_domain) { + case ATTR_DOMAIN_POINT: { + MutableSpan<float> selection = curves.selection_point_float_for_write(); + if (!was_anything_selected) { + selection.fill(1.0f); + } + if (partial) { + if (constant_per_curve) { + for (const int curve_i : curves.curves_range()) { + const float random_value = next_partial_random_value(); + const IndexRange points = curves.points_for_curve(curve_i); + for (const int point_i : points) { + selection[point_i] *= random_value; + } + } + } + else { + for (const int point_i : selection.index_range()) { + const float random_value = next_partial_random_value(); + selection[point_i] *= random_value; + } + } + } + else { + if (constant_per_curve) { + for (const int curve_i : curves.curves_range()) { + const bool random_value = next_bool_random_value(); + const IndexRange points = curves.points_for_curve(curve_i); + if (!random_value) { + selection.slice(points).fill(0.0f); + } + } + } + else { + for (const int point_i : selection.index_range()) { + const bool random_value = next_bool_random_value(); + if (!random_value) { + selection[point_i] = 0.0f; + } + } + } + } + break; + } + case ATTR_DOMAIN_CURVE: { + MutableSpan<float> selection = curves.selection_curve_float_for_write(); + if (!was_anything_selected) { + selection.fill(1.0f); + } + if (partial) { + for (const int curve_i : curves.curves_range()) { + const float random_value = next_partial_random_value(); + selection[curve_i] *= random_value; + } + } + else { + for (const int curve_i : curves.curves_range()) { + const bool random_value = next_bool_random_value(); + if (!random_value) { + selection[curve_i] = 0.0f; + } + } + } + break; + } + } + MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ? + curves.selection_point_float_for_write() : + curves.selection_curve_float_for_write(); + const bool was_any_selected = std::any_of( + selection.begin(), selection.end(), [](const float v) { return v > 0.0f; }); + if (was_any_selected) { + for (float &v : selection) { + v *= rng.get_float(); + } + } + else { + for (float &v : selection) { + v = rng.get_float(); + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + return OPERATOR_FINISHED; +} + +static void select_random_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + + uiItemR(layout, op->ptr, "seed", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "constant_per_curve", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "partial", 0, nullptr, ICON_NONE); + + if (RNA_boolean_get(op->ptr, "partial")) { + uiItemR(layout, op->ptr, "min", UI_ITEM_R_SLIDER, "Min", ICON_NONE); + } + else { + uiItemR(layout, op->ptr, "probability", UI_ITEM_R_SLIDER, "Probability", ICON_NONE); + } +} + +} // namespace select_random + +static void SCULPT_CURVES_OT_select_random(wmOperatorType *ot) +{ + ot->name = "Select Random"; + ot->idname = __func__; + ot->description = "Randomizes existing selection or create new random selection"; + + ot->exec = select_random::select_random_exec; + ot->poll = curves::editable_curves_poll; + ot->ui = select_random::select_random_ui; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_int(ot->srna, + "seed", + 0, + INT32_MIN, + INT32_MAX, + "Seed", + "Source of randomness", + INT32_MIN, + INT32_MAX); + RNA_def_boolean( + ot->srna, "partial", false, "Partial", "Allow points or curves to be selected partially"); + RNA_def_float(ot->srna, + "probability", + 0.5f, + 0.0f, + 1.0f, + "Probability", + "Chance of every point or curve being included in the selection", + 0.0f, + 1.0f); + RNA_def_float(ot->srna, + "min", + 0.0f, + 0.0f, + 1.0f, + "Min", + "Minimum value for the random selection", + 0.0f, + 1.0f); + RNA_def_boolean(ot->srna, + "constant_per_curve", + true, + "Constant per Curve", + "The generated random number is the same for every control point of a curve"); +} + +namespace select_end { +static bool select_end_poll(bContext *C) +{ + if (!curves::editable_curves_poll(C)) { + return false; + } + const Curves *curves_id = static_cast<const Curves *>(CTX_data_active_object(C)->data); + if (curves_id->selection_domain != ATTR_DOMAIN_POINT) { + CTX_wm_operator_poll_msg_set(C, "Only available in point selection mode"); + return false; + } + return true; +} + +static int select_end_exec(bContext *C, wmOperator *op) +{ + VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C); + const bool end_points = RNA_boolean_get(op->ptr, "end_points"); + const int amount = RNA_int_get(op->ptr, "amount"); + + for (Curves *curves_id : unique_curves) { + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + const bool was_anything_selected = curves::has_anything_selected(*curves_id); + MutableSpan<float> selection = curves.selection_point_float_for_write(); + if (!was_anything_selected) { + selection.fill(1.0f); + } + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = curves.points_for_curve(curve_i); + if (end_points) { + selection.slice(points.drop_back(amount)).fill(0.0f); + } + else { + selection.slice(points.drop_front(amount)).fill(0.0f); + } + } + }); + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + return OPERATOR_FINISHED; +} +} // namespace select_end + +static void SCULPT_CURVES_OT_select_end(wmOperatorType *ot) +{ + ot->name = "Select End"; + ot->idname = __func__; + ot->description = "Select end points of curves"; + + ot->exec = select_end::select_end_exec; + ot->poll = select_end::select_end_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, + "end_points", + true, + "End Points", + "Select points at the end of the curve as opposed to the beginning"); + RNA_def_int( + ot->srna, "amount", 1, 0, INT32_MAX, "Amount", "Number of points to select", 0, INT32_MAX); +} + +namespace select_grow { + +struct GrowOperatorDataPerCurve : NonCopyable, NonMovable { + Curves *curves_id; + Vector<int> selected_point_indices; + Vector<int> unselected_point_indices; + Array<float> distances_to_selected; + Array<float> distances_to_unselected; + + Array<float> original_selection; + float pixel_to_distance_factor; +}; + +struct GrowOperatorData { + int initial_mouse_x; + Vector<std::unique_ptr<GrowOperatorDataPerCurve>> per_curve; +}; + +static void update_points_selection(const GrowOperatorDataPerCurve &data, + const float distance, + MutableSpan<float> points_selection) +{ + if (distance > 0.0f) { + threading::parallel_for( + data.unselected_point_indices.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int point_i = data.unselected_point_indices[i]; + const float distance_to_selected = data.distances_to_selected[i]; + const float selection = distance_to_selected <= distance ? 1.0f : 0.0f; + points_selection[point_i] = selection; + } + }); + threading::parallel_for( + data.selected_point_indices.index_range(), 512, [&](const IndexRange range) { + for (const int point_i : data.selected_point_indices.as_span().slice(range)) { + points_selection[point_i] = 1.0f; + } + }); + } + else { + threading::parallel_for( + data.selected_point_indices.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int point_i = data.selected_point_indices[i]; + const float distance_to_unselected = data.distances_to_unselected[i]; + const float selection = distance_to_unselected <= -distance ? 0.0f : 1.0f; + points_selection[point_i] = selection; + } + }); + threading::parallel_for( + data.unselected_point_indices.index_range(), 512, [&](const IndexRange range) { + for (const int point_i : data.unselected_point_indices.as_span().slice(range)) { + points_selection[point_i] = 0.0f; + } + }); + } +} + +static int select_grow_update(bContext *C, wmOperator *op, const float mouse_diff_x) +{ + GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata); + + for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) { + Curves &curves_id = *curve_op_data->curves_id; + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + const float distance = curve_op_data->pixel_to_distance_factor * mouse_diff_x; + + /* Grow or shrink selection based on precomputed distances. */ + switch (curves_id.selection_domain) { + case ATTR_DOMAIN_POINT: { + MutableSpan<float> points_selection = curves.selection_point_float_for_write(); + update_points_selection(*curve_op_data, distance, points_selection); + break; + } + case ATTR_DOMAIN_CURVE: { + Array<float> new_points_selection(curves.points_num()); + update_points_selection(*curve_op_data, distance, new_points_selection); + /* Propagate grown point selection to the curve selection. */ + MutableSpan<float> curves_selection = curves.selection_curve_float_for_write(); + for (const int curve_i : curves.curves_range()) { + const IndexRange points = curves.points_for_curve(curve_i); + const Span<float> points_selection = new_points_selection.as_span().slice(points); + const float max_selection = *std::max_element(points_selection.begin(), + points_selection.end()); + curves_selection[curve_i] = max_selection; + } + break; + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id); + } + + return OPERATOR_FINISHED; +} + +static void select_grow_invoke_per_curve(Curves &curves_id, + Object &curves_ob, + const ARegion ®ion, + const View3D &v3d, + const RegionView3D &rv3d, + GrowOperatorDataPerCurve &curve_op_data) +{ + curve_op_data.curves_id = &curves_id; + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + const Span<float3> positions = curves.positions(); + + /* Find indices of selected and unselected points. */ + switch (curves_id.selection_domain) { + case ATTR_DOMAIN_POINT: { + const VArray<float> points_selection = curves.selection_point_float(); + curve_op_data.original_selection.reinitialize(points_selection.size()); + points_selection.materialize(curve_op_data.original_selection); + for (const int point_i : points_selection.index_range()) { + const float point_selection = points_selection[point_i]; + if (point_selection > 0.0f) { + curve_op_data.selected_point_indices.append(point_i); + } + else { + curve_op_data.unselected_point_indices.append(point_i); + } + } + + break; + } + case ATTR_DOMAIN_CURVE: { + const VArray<float> curves_selection = curves.selection_curve_float(); + curve_op_data.original_selection.reinitialize(curves_selection.size()); + curves_selection.materialize(curve_op_data.original_selection); + for (const int curve_i : curves_selection.index_range()) { + const float curve_selection = curves_selection[curve_i]; + const IndexRange points = curves.points_for_curve(curve_i); + if (curve_selection > 0.0f) { + for (const int point_i : points) { + curve_op_data.selected_point_indices.append(point_i); + } + } + else { + for (const int point_i : points) { + curve_op_data.unselected_point_indices.append(point_i); + } + } + } + break; + } + } + + threading::parallel_invoke( + 1024 < curve_op_data.selected_point_indices.size() + + curve_op_data.unselected_point_indices.size(), + [&]() { + /* Build KD-tree for the selected points. */ + KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.selected_point_indices.size()); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); }); + for (const int point_i : curve_op_data.selected_point_indices) { + const float3 &position = positions[point_i]; + BLI_kdtree_3d_insert(kdtree, point_i, position); + } + BLI_kdtree_3d_balance(kdtree); + + /* For each unselected point, compute the distance to the closest selected point. */ + curve_op_data.distances_to_selected.reinitialize( + curve_op_data.unselected_point_indices.size()); + threading::parallel_for(curve_op_data.unselected_point_indices.index_range(), + 256, + [&](const IndexRange range) { + for (const int i : range) { + const int point_i = curve_op_data.unselected_point_indices[i]; + const float3 &position = positions[point_i]; + KDTreeNearest_3d nearest; + BLI_kdtree_3d_find_nearest(kdtree, position, &nearest); + curve_op_data.distances_to_selected[i] = nearest.dist; + } + }); + }, + [&]() { + /* Build KD-tree for the unselected points. */ + KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.unselected_point_indices.size()); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); }); + for (const int point_i : curve_op_data.unselected_point_indices) { + const float3 &position = positions[point_i]; + BLI_kdtree_3d_insert(kdtree, point_i, position); + } + BLI_kdtree_3d_balance(kdtree); + + /* For each selected point, compute the distance to the closest unselected point. */ + curve_op_data.distances_to_unselected.reinitialize( + curve_op_data.selected_point_indices.size()); + threading::parallel_for( + curve_op_data.selected_point_indices.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int point_i = curve_op_data.selected_point_indices[i]; + const float3 &position = positions[point_i]; + KDTreeNearest_3d nearest; + BLI_kdtree_3d_find_nearest(kdtree, position, &nearest); + curve_op_data.distances_to_unselected[i] = nearest.dist; + } + }); + }); + + float4x4 curves_to_world_mat = curves_ob.obmat; + float4x4 world_to_curves_mat = curves_to_world_mat.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(&rv3d, &curves_ob, projection.values); + + /* Compute how mouse movements in screen space are converted into grow/shrink distances in + * object space. */ + curve_op_data.pixel_to_distance_factor = threading::parallel_reduce( + curve_op_data.selected_point_indices.index_range(), + 256, + FLT_MAX, + [&](const IndexRange range, float pixel_to_distance_factor) { + for (const int i : range) { + const int point_i = curve_op_data.selected_point_indices[i]; + const float3 &pos_cu = positions[point_i]; + + float2 pos_re; + ED_view3d_project_float_v2_m4(®ion, pos_cu, pos_re, projection.values); + if (pos_re.x < 0 || pos_re.y < 0 || pos_re.x > region.winx || pos_re.y > region.winy) { + continue; + } + /* Compute how far this point moves in curve space when it moves one unit in screen + * space. */ + const float2 pos_offset_re = pos_re + float2(1, 0); + float3 pos_offset_wo; + ED_view3d_win_to_3d( + &v3d, ®ion, curves_to_world_mat * pos_cu, pos_offset_re, pos_offset_wo); + const float3 pos_offset_cu = world_to_curves_mat * pos_offset_wo; + const float dist_cu = math::distance(pos_cu, pos_offset_cu); + const float dist_re = math::distance(pos_re, pos_offset_re); + const float factor = dist_cu / dist_re; + math::min_inplace(pixel_to_distance_factor, factor); + } + return pixel_to_distance_factor; + }, + [](const float a, const float b) { return std::min(a, b); }); +} + +static int select_grow_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *active_ob = CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + GrowOperatorData *op_data = MEM_new<GrowOperatorData>(__func__); + op->customdata = op_data; + + op_data->initial_mouse_x = event->xy[0]; + + Curves &curves_id = *static_cast<Curves *>(active_ob->data); + auto curve_op_data = std::make_unique<GrowOperatorDataPerCurve>(); + select_grow_invoke_per_curve(curves_id, *active_ob, *region, *v3d, *rv3d, *curve_op_data); + op_data->per_curve.append(std::move(curve_op_data)); + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata); + const int mouse_x = event->xy[0]; + const int mouse_diff_x = mouse_x - op_data.initial_mouse_x; + switch (event->type) { + case MOUSEMOVE: { + select_grow_update(C, op, mouse_diff_x); + break; + } + case LEFTMOUSE: { + MEM_delete(&op_data); + return OPERATOR_FINISHED; + } + case EVT_ESCKEY: + case RIGHTMOUSE: { + /* Undo operator by resetting the selection to the original value. */ + for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) { + Curves &curves_id = *curve_op_data->curves_id; + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + switch (curves_id.selection_domain) { + case ATTR_DOMAIN_POINT: { + MutableSpan<float> points_selection = curves.selection_point_float_for_write(); + points_selection.copy_from(curve_op_data->original_selection); + break; + } + case ATTR_DOMAIN_CURVE: { + MutableSpan<float> curves_seletion = curves.selection_curve_float_for_write(); + curves_seletion.copy_from(curve_op_data->original_selection); + break; + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id); + } + MEM_delete(&op_data); + return OPERATOR_CANCELLED; + } + } + return OPERATOR_RUNNING_MODAL; +} + +} // namespace select_grow + +static void SCULPT_CURVES_OT_select_grow(wmOperatorType *ot) +{ + ot->name = "Select Grow"; + ot->idname = __func__; + ot->description = "Select curves which are close to curves that are selected already"; + + ot->invoke = select_grow::select_grow_invoke; + ot->modal = select_grow::select_grow_modal; + ot->poll = curves::editable_curves_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + PropertyRNA *prop; + prop = RNA_def_float(ot->srna, + "distance", + 0.1f, + -FLT_MAX, + FLT_MAX, + "Distance", + "By how much to grow the selection", + -10.0f, + 10.f); + RNA_def_property_subtype(prop, PROP_DISTANCE); +} + +namespace min_distance_edit { + +static bool min_distance_edit_poll(bContext *C) +{ + if (!curves::curves_with_surface_poll(C)) { + return false; + } + Scene *scene = CTX_data_scene(C); + const Brush *brush = BKE_paint_brush_for_read(&scene->toolsettings->curves_sculpt->paint); + if (brush == nullptr) { + return false; + } + if (brush->curves_sculpt_tool != CURVES_SCULPT_TOOL_DENSITY) { + return false; + } + return true; +} + +struct MinDistanceEditData { + /** Brush whose minimum distance is modified. */ + Brush *brush; + float4x4 curves_to_world_mat; + + /** Where the preview is drawn. */ + float3 pos_cu; + float3 normal_cu; + + int2 initial_mouse; + float initial_minimum_distance; + + /** The operator uses a new cursor, but the existing cursors should be restored afterwards. */ + ListBase orig_paintcursors; + void *cursor; + + /** Store the viewport region in case the operator was called from the header. */ + ARegion *region; + RegionView3D *rv3d; +}; + +static int calculate_points_per_side(bContext *C, MinDistanceEditData &op_data) +{ + Scene *scene = CTX_data_scene(C); + ARegion *region = op_data.region; + + const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance; + const float brush_radius = BKE_brush_size_get(scene, op_data.brush); + + float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1}); + if (math::is_zero(tangent_x_cu)) { + tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0}); + } + tangent_x_cu = math::normalize(tangent_x_cu); + const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu)); + + /* Sample a few points to get a good estimate of how large the grid has to be. */ + Vector<float3> points_wo; + points_wo.append(op_data.pos_cu + min_distance * tangent_x_cu); + points_wo.append(op_data.pos_cu + min_distance * tangent_y_cu); + points_wo.append(op_data.pos_cu - min_distance * tangent_x_cu); + points_wo.append(op_data.pos_cu - min_distance * tangent_y_cu); + + Vector<float2> points_re; + for (const float3 &pos_wo : points_wo) { + float2 pos_re; + ED_view3d_project_v2(region, pos_wo, pos_re); + points_re.append(pos_re); + } + + float2 origin_re; + ED_view3d_project_v2(region, op_data.pos_cu, origin_re); + + int needed_points = 0; + for (const float2 &pos_re : points_re) { + const float distance = math::length(pos_re - origin_re); + const int needed_points_iter = (brush_radius * 2.0f) / distance; + + if (needed_points_iter > needed_points) { + needed_points = needed_points_iter; + } + } + + /* Limit to a hard-coded number since it only adds noise at some point. */ + return std::min(300, needed_points); +} + +static void min_distance_edit_draw(bContext *C, int UNUSED(x), int UNUSED(y), void *customdata) +{ + Scene *scene = CTX_data_scene(C); + MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(customdata); + + const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance; + + float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1}); + if (math::is_zero(tangent_x_cu)) { + tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0}); + } + tangent_x_cu = math::normalize(tangent_x_cu); + const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu)); + + const int points_per_side = calculate_points_per_side(C, op_data); + const int points_per_axis_num = 2 * points_per_side + 1; + + Vector<float3> points_wo; + for (const int x_i : IndexRange(points_per_axis_num)) { + for (const int y_i : IndexRange(points_per_axis_num)) { + const float x_iter = min_distance * (x_i - (points_per_axis_num - 1) / 2.0f); + const float y_iter = min_distance * (y_i - (points_per_axis_num - 1) / 2.0f); + + const float3 point_pos_cu = op_data.pos_cu + op_data.normal_cu * 0.0001f + + x_iter * tangent_x_cu + y_iter * tangent_y_cu; + const float3 point_pos_wo = op_data.curves_to_world_mat * point_pos_cu; + points_wo.append(point_pos_wo); + } + } + + float4 circle_col = float4(op_data.brush->add_col); + float circle_alpha = op_data.brush->cursor_overlay_alpha; + float brush_radius_re = BKE_brush_size_get(scene, op_data.brush); + + /* Draw the grid. */ + GPU_matrix_push(); + GPU_matrix_push_projection(); + GPU_blend(GPU_BLEND_ALPHA); + + ARegion *region = op_data.region; + RegionView3D *rv3d = op_data.rv3d; + wmWindow *win = CTX_wm_window(C); + + /* It does the same as: `view3d_operator_needs_opengl(C);`. */ + wmViewport(®ion->winrct); + GPU_matrix_projection_set(rv3d->winmat); + GPU_matrix_set(rv3d->viewmat); + + GPUVertFormat *format3d = immVertexFormat(); + + const uint pos3d = GPU_vertformat_attr_add(format3d, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint col3d = GPU_vertformat_attr_add(format3d, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_POINT_FIXED_SIZE_VARYING_COLOR); + + GPU_point_size(3.0f); + immBegin(GPU_PRIM_POINTS, points_wo.size()); + + float3 brush_origin_wo = op_data.curves_to_world_mat * op_data.pos_cu; + float2 brush_origin_re; + ED_view3d_project_v2(region, brush_origin_wo, brush_origin_re); + + /* Smooth alpha transition until the brush edge. */ + const int alpha_border_re = 20; + const float dist_to_inner_border_re = brush_radius_re - alpha_border_re; + + for (const float3 &pos_wo : points_wo) { + float2 pos_re; + ED_view3d_project_v2(region, pos_wo, pos_re); + + const float dist_to_point_re = math::distance(pos_re, brush_origin_re); + const float alpha = 1.0f - ((dist_to_point_re - dist_to_inner_border_re) / alpha_border_re); + + immAttr4f(col3d, 0.9f, 0.9f, 0.9f, alpha); + immVertex3fv(pos3d, pos_wo); + } + immEnd(); + immUnbindProgram(); + + /* Reset the drawing settings. */ + GPU_point_size(1.0f); + GPU_matrix_pop_projection(); + GPU_matrix_pop(); + + int4 scissor; + GPU_scissor_get(scissor); + wmWindowViewport(win); + 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); + + GPUVertFormat *format = immVertexFormat(); + uint pos2d = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + immUniformColor3fvAlpha(circle_col, circle_alpha); + imm_draw_circle_wire_2d(pos2d, 0.0f, 0.0f, brush_radius_re, 80); + + immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); +} + +static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + Scene *scene = CTX_data_scene(C); + + Object &curves_ob_orig = *CTX_data_active_object(C); + Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data); + Object &surface_ob_orig = *curves_id_orig.surface; + Object *surface_ob_eval = DEG_get_evaluated_object(depsgraph, &surface_ob_orig); + if (surface_ob_eval == nullptr) { + return OPERATOR_CANCELLED; + } + Mesh *surface_me_eval = BKE_object_get_evaluated_mesh(surface_ob_eval); + if (surface_me_eval == nullptr) { + return OPERATOR_CANCELLED; + } + + BVHTreeFromMesh surface_bvh_eval; + BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_me_eval, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); }); + + const int2 mouse_pos_int_re{event->mval}; + const float2 mouse_pos_re{mouse_pos_int_re}; + + float3 ray_start_wo, ray_end_wo; + ED_view3d_win_to_segment_clipped( + depsgraph, region, v3d, mouse_pos_re, ray_start_wo, ray_end_wo, true); + + const CurvesSurfaceTransforms transforms{curves_ob_orig, &surface_ob_orig}; + + const float3 ray_start_su = transforms.world_to_surface * ray_start_wo; + const float3 ray_end_su = transforms.world_to_surface * ray_end_wo; + const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); + + BVHTreeRayHit ray_hit; + ray_hit.dist = FLT_MAX; + ray_hit.index = -1; + BLI_bvhtree_ray_cast(surface_bvh_eval.tree, + ray_start_su, + ray_direction_su, + 0.0f, + &ray_hit, + surface_bvh_eval.raycast_callback, + &surface_bvh_eval); + if (ray_hit.index == -1) { + WM_report(RPT_ERROR, "Cursor must be over the surface mesh"); + return OPERATOR_CANCELLED; + } + + const float3 hit_pos_su = ray_hit.co; + const float3 hit_normal_su = ray_hit.no; + + const float3 hit_pos_cu = transforms.surface_to_curves * hit_pos_su; + const float3 hit_normal_cu = math::normalize(transforms.surface_to_curves_normal * + hit_normal_su); + + MinDistanceEditData *op_data = MEM_new<MinDistanceEditData>(__func__); + op_data->curves_to_world_mat = transforms.curves_to_world; + op_data->normal_cu = hit_normal_cu; + op_data->pos_cu = hit_pos_cu; + op_data->initial_mouse = event->xy; + op_data->brush = BKE_paint_brush(&scene->toolsettings->curves_sculpt->paint); + op_data->initial_minimum_distance = op_data->brush->curves_sculpt_settings->minimum_distance; + + if (op_data->initial_minimum_distance <= 0.0f) { + op_data->initial_minimum_distance = 0.01f; + } + + op->customdata = op_data; + + /* Temporarily disable other paint cursors. */ + wmWindowManager *wm = CTX_wm_manager(C); + op_data->orig_paintcursors = wm->paintcursors; + BLI_listbase_clear(&wm->paintcursors); + + /* Add minimum distance paint cursor. */ + op_data->cursor = WM_paint_cursor_activate( + SPACE_TYPE_ANY, RGN_TYPE_ANY, op->type->poll, min_distance_edit_draw, op_data); + + op_data->region = CTX_wm_region(C); + op_data->rv3d = CTX_wm_region_view3d(C); + + WM_event_add_modal_handler(C, op); + ED_region_tag_redraw(region); + return OPERATOR_RUNNING_MODAL; +} + +static int min_distance_edit_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(op->customdata); + + auto finish = [&]() { + wmWindowManager *wm = CTX_wm_manager(C); + + /* Remove own cursor. */ + WM_paint_cursor_end(static_cast<wmPaintCursor *>(op_data.cursor)); + /* Restore original paint cursors. */ + wm->paintcursors = op_data.orig_paintcursors; + + ED_region_tag_redraw(region); + MEM_freeN(&op_data); + }; + + switch (event->type) { + case MOUSEMOVE: { + const int2 mouse_pos_int_re{event->xy}; + const float2 mouse_pos_re{mouse_pos_int_re}; + + const float mouse_diff_x = mouse_pos_int_re.x - op_data.initial_mouse.x; + const float factor = powf(2, mouse_diff_x / UI_UNIT_X / 10.0f); + op_data.brush->curves_sculpt_settings->minimum_distance = op_data.initial_minimum_distance * + factor; + + ED_region_tag_redraw(region); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); + break; + } + case LEFTMOUSE: { + if (event->val == KM_PRESS) { + finish(); + return OPERATOR_FINISHED; + } + break; + } + case RIGHTMOUSE: + case EVT_ESCKEY: { + op_data.brush->curves_sculpt_settings->minimum_distance = op_data.initial_minimum_distance; + finish(); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); + return OPERATOR_CANCELLED; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +} // namespace min_distance_edit + +static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) +{ + ot->name = "Edit Minimum Distance"; + ot->idname = __func__; + ot->description = "Change the minimum distance used by the density brush"; + + ot->poll = min_distance_edit::min_distance_edit_poll; + ot->invoke = min_distance_edit::min_distance_edit_invoke; + ot->modal = min_distance_edit::min_distance_edit_modal; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; +} + } // namespace blender::ed::sculpt_paint /* -------------------------------------------------------------------- */ @@ -333,6 +1272,10 @@ void ED_operatortypes_sculpt_curves() using namespace blender::ed::sculpt_paint; WM_operatortype_append(SCULPT_CURVES_OT_brush_stroke); WM_operatortype_append(CURVES_OT_sculptmode_toggle); + WM_operatortype_append(SCULPT_CURVES_OT_select_random); + WM_operatortype_append(SCULPT_CURVES_OT_select_end); + WM_operatortype_append(SCULPT_CURVES_OT_select_grow); + WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc new file mode 100644 index 00000000000..3e43b1a6361 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BLI_float4x4.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DEG_depsgraph.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_paint.h" + +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "WM_api.h" + +/** + * The code below uses a prefix naming convention to indicate the coordinate space: + * `cu`: Local space of the curves object that is being edited. + * `su`: Local space of the surface object. + * `wo`: World space. + * `re`: 2D coordinates within the region. + */ + +namespace blender::ed::sculpt_paint { + +class PinchOperation : public CurvesSculptStrokeOperation { + private: + bool invert_pinch_; + Array<float> segment_lengths_cu_; + + /** Only used when a 3D brush is used. */ + CurvesBrush3D brush_3d_; + + friend struct PinchOperationExecutor; + + public: + PinchOperation(const bool invert_pinch) : invert_pinch_(invert_pinch) + { + } + + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +struct PinchOperationExecutor { + PinchOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + VArray<float> point_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + CurvesSurfaceTransforms transforms_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + + float invert_factor_; + + float2 brush_pos_re_; + + PinchOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(PinchOperation &self, const bContext &C, const StrokeExtension &stroke_extension) + { + self_ = &self; + + object_ = CTX_data_active_object(&C); + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = BKE_brush_alpha_get(ctx_.scene, brush_); + + invert_factor_ = self_->invert_pinch_ ? -1.0f : 1.0f; + + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + + point_factors_ = get_point_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + + brush_pos_re_ = stroke_extension.mouse_position; + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + + if (stroke_extension.is_first) { + this->initialize_segment_lengths(); + + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + self_->brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); + } + } + + Array<bool> changed_curves(curves_->curves_num(), false); + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->pinch_projected_with_symmetry(changed_curves); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->pinch_spherical_with_symmetry(changed_curves); + } + else { + BLI_assert_unreachable(); + } + + this->restore_segment_lengths(changed_curves); + curves_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); + } + + void pinch_projected_with_symmetry(MutableSpan<bool> r_changed_curves) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->pinch_projected(brush_transform, r_changed_curves); + } + } + + void pinch_projected(const float4x4 &brush_transform, MutableSpan<bool> r_changed_curves) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_front(1)) { + const float3 old_pos_cu = deformation.positions[point_i]; + const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; + float2 old_symm_pos_re; + ED_view3d_project_float_v2_m4( + ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values); + + const float dist_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_re_); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float t = safe_divide(dist_to_brush_re, brush_radius_base_re_); + const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f); + const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff * + point_factors_[point_i]; + + const float2 new_symm_pos_re = math::interpolate(old_symm_pos_re, brush_pos_re_, weight); + + float3 new_symm_pos_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * old_symm_pos_cu, + new_symm_pos_re, + new_symm_pos_wo); + + const float3 new_pos_cu = brush_transform * transforms_.world_to_curves * + new_symm_pos_wo; + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + point_i, translation_eval); + positions_cu[point_i] += translation_orig; + r_changed_curves[curve_i] = true; + } + } + }); + } + + void pinch_spherical_with_symmetry(MutableSpan<bool> r_changed_curves) + { + float3 brush_pos_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_pos_wo); + const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->pinch_spherical(brush_transform * brush_pos_cu, brush_radius_cu, r_changed_curves); + } + } + + void pinch_spherical(const float3 &brush_pos_cu, + const float brush_radius_cu, + MutableSpan<bool> r_changed_curves) + { + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_front(1)) { + const float3 old_pos_cu = deformation.positions[point_i]; + + const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float t = safe_divide(dist_to_brush_cu, brush_radius_cu); + const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f); + const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff * + point_factors_[point_i]; + + const float3 new_pos_cu = math::interpolate(old_pos_cu, brush_pos_cu, weight); + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + point_i, translation_eval); + positions_cu[point_i] += translation_orig; + + r_changed_curves[curve_i] = true; + } + } + }); + } + + void initialize_segment_lengths() + { + const Span<float3> positions_cu = curves_->positions(); + self_->segment_lengths_cu_.reinitialize(curves_->points_num()); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[point_i]; + const float3 &p2_cu = positions_cu[point_i + 1]; + const float length_cu = math::distance(p1_cu, p2_cu); + self_->segment_lengths_cu_[point_i] = length_cu; + } + } + }); + } + + void restore_segment_lengths(const Span<bool> changed_curves) + { + const Span<float> expected_lengths_cu = self_->segment_lengths_cu_; + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + + threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + if (!changed_curves[curve_i]) { + continue; + } + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int segment_i : IndexRange(points.size() - 1)) { + const float3 &p1_cu = positions_cu[points[segment_i]]; + float3 &p2_cu = positions_cu[points[segment_i] + 1]; + const float3 direction = math::normalize(p2_cu - p1_cu); + const float expected_length_cu = expected_lengths_cu[points[segment_i]]; + p2_cu = p1_cu + direction * expected_length_cu; + } + } + }); + } +}; + +void PinchOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) +{ + PinchOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStrokeMode brush_mode, + const bContext &C) +{ + const Scene &scene = *CTX_data_scene(&C); + const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint); + + const bool invert_pinch = (brush_mode == BRUSH_STROKE_INVERT) != + ((brush.flag & BRUSH_DIR_IN) != 0); + return std::make_unique<PinchOperation>(invert_pinch); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc new file mode 100644 index 00000000000..ec69aae372c --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc @@ -0,0 +1,385 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_crazyspace.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "WM_api.h" + +#include "BLI_length_parameterize.hh" + +#include "GEO_add_curves_on_mesh.hh" + +#include "curves_sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { + +class PuffOperation : public CurvesSculptStrokeOperation { + private: + /** Only used when a 3D brush is used. */ + CurvesBrush3D brush_3d_; + + /** Length of each segment indexed by the index of the first point in the segment. */ + Array<float> segment_lengths_cu_; + + friend struct PuffOperationExecutor; + + public: + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct PuffOperationExecutor { + PuffOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + VArray<float> point_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + float2 brush_pos_re_; + + eBrushFalloffShape falloff_shape_; + + CurvesSurfaceTransforms transforms_; + + Object *surface_ob_ = nullptr; + Mesh *surface_ = nullptr; + Span<MVert> surface_verts_; + Span<MLoop> surface_loops_; + Span<MLoopTri> surface_looptris_; + Span<float3> corner_normals_su_; + BVHTreeFromMesh surface_bvh_; + + PuffOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(PuffOperation &self, const bContext &C, const StrokeExtension &stroke_extension) + { + UNUSED_VARS(C, stroke_extension); + self_ = &self; + + object_ = CTX_data_active_object(&C); + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } + if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) { + return; + } + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + brush_pos_re_ = stroke_extension.mouse_position; + + point_factors_ = get_point_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + + falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); + + surface_ob_ = curves_id_->surface; + surface_ = static_cast<Mesh *>(surface_ob_->data); + + transforms_ = CurvesSurfaceTransforms(*object_, surface_ob_); + + if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_); + } + corner_normals_su_ = { + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), + surface_->totloop}; + + surface_verts_ = surface_->verts(); + surface_loops_ = surface_->loops(); + surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), + BKE_mesh_runtime_looptri_len(surface_)}; + BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); + + if (stroke_extension.is_first) { + this->initialize_segment_lengths(); + if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { + self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); + } + } + + Array<float> curve_weights(curve_selection_.size(), 0.0f); + + if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { + this->find_curve_weights_projected_with_symmetry(curve_weights); + } + else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { + this->find_curves_weights_spherical_with_symmetry(curve_weights); + } + else { + BLI_assert_unreachable(); + } + + this->puff(curve_weights); + this->restore_segment_lengths(); + + curves_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); + } + + void find_curve_weights_projected_with_symmetry(MutableSpan<float> r_curve_weights) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->find_curve_weights_projected(brush_transform, r_curve_weights); + } + } + + void find_curve_weights_projected(const float4x4 &brush_transform, + MutableSpan<float> r_curve_weights) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_selection_i : range) { + const int curve_i = curve_selection_[curve_selection_i]; + const IndexRange points = curves_->points_for_curve(curve_i); + const float3 first_pos_cu = brush_transform_inv * deformation.positions[points[0]]; + float2 prev_pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, first_pos_cu, prev_pos_re, projection.values); + for (const int point_i : points.drop_front(1)) { + const float3 pos_cu = brush_transform_inv * deformation.positions[point_i]; + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + BLI_SCOPED_DEFER([&]() { prev_pos_re = pos_re; }); + + const float dist_to_brush_sq_re = dist_squared_to_line_segment_v2( + brush_pos_re_, prev_pos_re, pos_re); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re); + const float weight = radius_falloff; + math::max_inplace(r_curve_weights[curve_selection_i], weight); + } + } + }); + } + + void find_curves_weights_spherical_with_symmetry(MutableSpan<float> r_curve_weights) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + float3 brush_pos_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_pos_wo); + const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->find_curves_weights_spherical( + brush_transform * brush_pos_cu, brush_radius_cu, r_curve_weights); + } + } + + void find_curves_weights_spherical(const float3 &brush_pos_cu, + const float brush_radius_cu, + MutableSpan<float> r_curve_weights) + { + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_selection_i : range) { + const int curve_i = curve_selection_[curve_selection_i]; + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_front(1)) { + const float3 &prev_pos_cu = deformation.positions[point_i - 1]; + const float3 &pos_cu = deformation.positions[point_i]; + const float dist_to_brush_sq_cu = dist_squared_to_line_segment_v3( + brush_pos_cu, prev_pos_cu, pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + const float weight = radius_falloff; + math::max_inplace(r_curve_weights[curve_selection_i], weight); + } + } + }); + } + + void puff(const Span<float> curve_weights) + { + BLI_assert(curve_weights.size() == curve_selection_.size()); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + Vector<float> accumulated_lengths_cu; + for (const int curve_selection_i : range) { + const int curve_i = curve_selection_[curve_selection_i]; + const IndexRange points = curves_->points_for_curve(curve_i); + const int first_point_i = points[0]; + const float3 first_pos_cu = positions_cu[first_point_i]; + const float3 first_pos_su = transforms_.curves_to_surface * first_pos_cu; + + /* Find the nearest position on the surface. The curve will be aligned to the normal of + * that point. */ + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest(surface_bvh_.tree, + first_pos_su, + &nearest, + surface_bvh_.nearest_callback, + &surface_bvh_); + + const MLoopTri &looptri = surface_looptris_[nearest.index]; + const float3 closest_pos_su = nearest.co; + const float3 &v0_su = surface_verts_[surface_loops_[looptri.tri[0]].v].co; + const float3 &v1_su = surface_verts_[surface_loops_[looptri.tri[1]].v].co; + const float3 &v2_su = surface_verts_[surface_loops_[looptri.tri[2]].v].co; + float3 bary_coords; + interp_weights_tri_v3(bary_coords, v0_su, v1_su, v2_su, closest_pos_su); + const float3 normal_su = geometry::compute_surface_point_normal( + looptri, bary_coords, corner_normals_su_); + const float3 normal_cu = math::normalize(transforms_.surface_to_curves_normal * normal_su); + + accumulated_lengths_cu.reinitialize(points.size() - 1); + length_parameterize::accumulate_lengths<float3>( + positions_cu.slice(points), false, accumulated_lengths_cu); + + /* Align curve to the surface normal while making sure that the curve does not fold up much + * in the process (e.g. when the curve was pointing in the opposite direction before). */ + for (const int i : IndexRange(points.size()).drop_front(1)) { + const int point_i = points[i]; + const float3 old_pos_cu = positions_cu[point_i]; + + /* Compute final position of the point. */ + const float length_param_cu = accumulated_lengths_cu[i - 1]; + const float3 goal_pos_cu = first_pos_cu + length_param_cu * normal_cu; + + const float weight = 0.01f * brush_strength_ * point_factors_[point_i] * + curve_weights[curve_selection_i]; + float3 new_pos_cu = math::interpolate(old_pos_cu, goal_pos_cu, weight); + + /* Make sure the point does not move closer to the root point than it was initially. This + * makes the curve kind of "rotate up". */ + const float old_dist_to_root_cu = math::distance(old_pos_cu, first_pos_cu); + const float new_dist_to_root_cu = math::distance(new_pos_cu, first_pos_cu); + if (new_dist_to_root_cu < old_dist_to_root_cu) { + const float3 offset = math::normalize(new_pos_cu - first_pos_cu); + new_pos_cu += (old_dist_to_root_cu - new_dist_to_root_cu) * offset; + } + + positions_cu[point_i] = new_pos_cu; + } + } + }); + } + + void initialize_segment_lengths() + { + const Span<float3> positions_cu = curves_->positions(); + self_->segment_lengths_cu_.reinitialize(curves_->points_num()); + threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[point_i]; + const float3 &p2_cu = positions_cu[point_i + 1]; + const float length_cu = math::distance(p1_cu, p2_cu); + self_->segment_lengths_cu_[point_i] = length_cu; + } + } + }); + } + + void restore_segment_lengths() + { + const Span<float> expected_lengths_cu = self_->segment_lengths_cu_; + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[segment_i]; + float3 &p2_cu = positions_cu[segment_i + 1]; + const float3 direction = math::normalize(p2_cu - p1_cu); + const float expected_length_cu = expected_lengths_cu[segment_i]; + p2_cu = p1_cu + direction * expected_length_cu; + } + } + }); + } +}; + +void PuffOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) +{ + PuffOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_puff_operation() +{ + return std::make_unique<PuffOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc index f620fed5761..a955a074df2 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc @@ -6,6 +6,8 @@ #include "curves_sculpt_intern.hh" +#include "ED_curves_sculpt.h" + namespace blender::ed::sculpt_paint { static VArray<float> get_curves_selection(const CurvesGeometry &curves, const eAttrDomain domain) @@ -62,13 +64,14 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves, case ATTR_DOMAIN_POINT: { const VArray<float> selection = curves.selection_point_float(); if (selection.is_single()) { - return selection.get_internal_single() == 0.0f ? IndexMask(0) : + return selection.get_internal_single() <= 0.0f ? IndexMask(0) : IndexMask(curves.curves_num()); } + const Span<float> point_selection_span = selection.get_internal_span(); return index_mask_ops::find_indices_based_on_predicate( curves.curves_range(), 512, r_indices, [&](const int curve_i) { for (const int i : curves.points_for_curve(curve_i)) { - if (selection[i] > 0.0f) { + if (point_selection_span[i] > 0.0f) { return true; } } @@ -78,7 +81,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves, case ATTR_DOMAIN_CURVE: { const VArray<float> selection = curves.selection_curve_float(); if (selection.is_single()) { - return selection.get_internal_single() == 0.0f ? IndexMask(0) : + return selection.get_internal_single() <= 0.0f ? IndexMask(0) : IndexMask(curves.curves_num()); } return index_mask_ops::find_indices_based_on_predicate( @@ -102,4 +105,49 @@ IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_i r_indices); } +static IndexMask retrieve_selected_points(const CurvesGeometry &curves, + const eAttrDomain domain, + Vector<int64_t> &r_indices) +{ + switch (domain) { + case ATTR_DOMAIN_POINT: { + const VArray<float> selection = curves.selection_point_float(); + if (selection.is_single()) { + return selection.get_internal_single() <= 0.0f ? IndexMask(0) : + IndexMask(curves.points_num()); + } + return index_mask_ops::find_indices_based_on_predicate( + curves.points_range(), 2048, r_indices, [&](const int i) { + return selection[i] > 0.0f; + }); + } + case ATTR_DOMAIN_CURVE: { + const VArray<float> selection = curves.selection_curve_float(); + if (selection.is_single()) { + return selection.get_internal_single() <= 0.0f ? IndexMask(0) : + IndexMask(curves.points_num()); + } + const VArray<float> point_selection = curves.adapt_domain( + selection, ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + return index_mask_ops::find_indices_based_on_predicate( + curves.points_range(), 2048, r_indices, [&](const int i) { + return point_selection[i] > 0.0f; + }); + } + default: + BLI_assert_unreachable(); + return {}; + } +} + +IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices) +{ + if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) { + return CurvesGeometry::wrap(curves_id.geometry).points_range(); + } + return retrieve_selected_points(CurvesGeometry::wrap(curves_id.geometry), + eAttrDomain(curves_id.selection_domain), + r_indices); +} + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc index 69615a3bfb4..cc5a5e7ae8a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc @@ -66,8 +66,7 @@ struct SelectionPaintOperationExecutor { float2 brush_pos_re_; - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; + CurvesSurfaceTransforms transforms_; SelectionPaintOperationExecutor(const bContext &C) : ctx_(C) { @@ -105,8 +104,7 @@ struct SelectionPaintOperationExecutor { } } - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); @@ -142,6 +140,7 @@ struct SelectionPaintOperationExecutor { * selection is handled as a generic attribute for now. */ DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ctx_.rv3d->rflag &= ~RV3D_PAINTING; ED_region_tag_redraw(ctx_.region); } @@ -162,14 +161,15 @@ struct SelectionPaintOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); - Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { for (const int point_i : point_range) { - const float3 pos_cu = brush_transform_inv * positions_cu[point_i]; + const float3 pos_cu = brush_transform_inv * deformation.positions[point_i]; /* Find the position of the point in screen space. */ float2 pos_re; @@ -201,10 +201,10 @@ struct SelectionPaintOperationExecutor { float3 brush_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_re_, brush_wo); - const float3 brush_cu = world_to_curves_mat_ * brush_wo; + const float3 brush_cu = transforms_.world_to_curves * brush_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); @@ -216,14 +216,15 @@ struct SelectionPaintOperationExecutor { void paint_point_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) { - Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { for (const int i : point_range) { - const float3 pos_old_cu = positions_cu[i]; + const float3 pos_old_cu = deformation.positions[i]; /* Compute distance to the brush. */ const float distance_to_brush_sq_cu = math::distance_squared(pos_old_cu, brush_cu); @@ -257,9 +258,11 @@ struct SelectionPaintOperationExecutor { void paint_curve_selection_projected(const float4x4 &brush_transform, MutableSpan<float> selection) { - const Span<float3> positions_cu = curves_->positions(); const float4x4 brush_transform_inv = brush_transform.inverted(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); @@ -275,8 +278,8 @@ struct SelectionPaintOperationExecutor { [&](const IndexRange segment_range, const float init) { float max_weight = init; for (const int segment_i : segment_range) { - const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; - const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + const float3 pos1_cu = brush_transform_inv * deformation.positions[segment_i]; + const float3 pos2_cu = brush_transform_inv * deformation.positions[segment_i + 1]; float2 pos1_re; float2 pos2_re; @@ -309,10 +312,10 @@ struct SelectionPaintOperationExecutor { float3 brush_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_re_, brush_wo); - const float3 brush_cu = world_to_curves_mat_ * brush_wo; + const float3 brush_cu = transforms_.world_to_curves * brush_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); @@ -324,7 +327,8 @@ struct SelectionPaintOperationExecutor { void paint_curve_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) { - const Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); @@ -338,8 +342,8 @@ struct SelectionPaintOperationExecutor { [&](const IndexRange segment_range, const float init) { float max_weight = init; for (const int segment_i : segment_range) { - const float3 &pos1_cu = positions_cu[segment_i]; - const float3 &pos2_cu = positions_cu[segment_i + 1]; + const float3 &pos1_cu = deformation.positions[segment_i]; + const float3 &pos2_cu = deformation.positions[segment_i + 1]; const float distance_sq_cu = dist_squared_to_line_segment_v3( brush_cu, pos1_cu, pos2_cu); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc new file mode 100644 index 00000000000..1108f5c72a9 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc @@ -0,0 +1,498 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BLI_float3x3.hh" +#include "BLI_float4x4.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DEG_depsgraph.h" + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#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" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "UI_interface.h" + +#include "WM_api.h" + +#include "DEG_depsgraph_query.h" + +#include "GEO_add_curves_on_mesh.hh" +#include "GEO_reverse_uv_sampler.hh" + +#include "BLT_translation.h" + +namespace blender::ed::sculpt_paint { + +using geometry::ReverseUVSampler; + +struct SlideCurveInfo { + /** Index of the curve to slide. */ + int curve_i; + /** A weight based on the initial distance to the brush. */ + float radius_falloff; + /** + * Normal of the surface where the curve was attached. This is used to rotate the curve if it is + * moved to a place with a different normal. + */ + float3 initial_normal_cu; +}; + +struct SlideInfo { + /** The transform used for the curves below (e.g. for symmetry). */ + float4x4 brush_transform; + Vector<SlideCurveInfo> curves_to_slide; +}; + +class SlideOperation : public CurvesSculptStrokeOperation { + private: + float2 initial_brush_pos_re_; + /** Information about which curves to slide. This is initialized when the brush starts. */ + Vector<SlideInfo> slide_info_; + /** Positions of all curve points at the start of sliding. */ + Array<float3> initial_positions_cu_; + /** Deformed positions of all curve points at the start of sliding. */ + Array<float3> initial_deformed_positions_cu_; + + friend struct SlideOperationExecutor; + + public: + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct SlideOperationExecutor { + SlideOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; + + Object *surface_ob_orig_ = nullptr; + Mesh *surface_orig_ = nullptr; + Span<MLoopTri> surface_looptris_orig_; + VArraySpan<float2> surface_uv_map_orig_; + Span<float3> corner_normals_orig_su_; + + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + Span<MVert> surface_verts_eval_; + Span<MLoop> surface_loops_eval_; + Span<MLoopTri> surface_looptris_eval_; + VArraySpan<float2> surface_uv_map_eval_; + BVHTreeFromMesh surface_bvh_eval_; + + VArray<float> curve_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + float2 brush_pos_re_; + + CurvesSurfaceTransforms transforms_; + + std::atomic<bool> found_invalid_uv_mapping_{false}; + + SlideOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(SlideOperation &self, const bContext &C, const StrokeExtension &stroke_extension) + { + UNUSED_VARS(C, stroke_extension); + self_ = &self; + + curves_ob_orig_ = CTX_data_active_object(&C); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); + if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) { + report_missing_surface(stroke_extension.reports); + return; + } + if (curves_orig_->curves_num() == 0) { + return; + } + if (curves_id_orig_->surface_uv_map == nullptr) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (curves_orig_->surface_uv_coords().is_empty()) { + BKE_report(stroke_extension.reports, + RPT_WARNING, + TIP_("Curves do not have surface attachment information")); + return; + } + const StringRefNull uv_map_name = curves_id_orig_->surface_uv_map; + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + + curve_factors_ = get_curves_selection(*curves_id_orig_); + curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_); + + brush_pos_re_ = stroke_extension.mouse_position; + + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); + + surface_ob_orig_ = curves_id_orig_->surface; + surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data); + if (surface_orig_->totpoly == 0) { + 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_uv_map_orig_ = surface_orig_->attributes().lookup<float2>(uv_map_name, + ATTR_DOMAIN_CORNER); + if (surface_uv_map_orig_.is_empty()) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_orig_); + } + corner_normals_orig_su_ = { + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)), + surface_orig_->totloop}; + + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); + if (surface_eval_ == nullptr) { + return; + } + if (surface_eval_->totpoly == 0) { + 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_verts_eval_ = surface_eval_->verts(); + surface_loops_eval_ = surface_eval_->loops(); + surface_uv_map_eval_ = surface_eval_->attributes().lookup<float2>(uv_map_name, + ATTR_DOMAIN_CORNER); + if (surface_uv_map_eval_.is_empty()) { + report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); + return; + } + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); + + if (stroke_extension.is_first) { + self_->initial_brush_pos_re_ = brush_pos_re_; + /* Remember original and deformed positions of all points. Otherwise this information is lost + * when sliding starts, but it's still used. */ + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); + self_->initial_positions_cu_ = curves_orig_->positions(); + self_->initial_deformed_positions_cu_ = deformation.positions; + + /* First find all curves to slide. When the mouse moves, only those curves will be moved. */ + this->find_curves_to_slide_with_symmetry(); + return; + } + this->slide_with_symmetry(); + + if (found_invalid_uv_mapping_) { + BKE_report( + stroke_extension.reports, RPT_WARNING, TIP_("UV map or surface attachment is invalid")); + } + + curves_orig_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); + ED_region_tag_redraw(ctx_.region); + } + + void find_curves_to_slide_with_symmetry() + { + const Vector<float4x4> brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_orig_->symmetry)); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + transforms_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re); + if (!brush_3d.has_value()) { + return; + } + const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_}; + for (const float4x4 &brush_transform : brush_transforms) { + self_->slide_info_.append_as(); + SlideInfo &slide_info = self_->slide_info_.last(); + slide_info.brush_transform = brush_transform; + this->find_curves_to_slide(brush_transform * brush_3d->position_cu, + brush_3d->radius_cu, + reverse_uv_sampler_orig, + slide_info.curves_to_slide); + } + } + + void find_curves_to_slide(const float3 &brush_pos_cu, + const float brush_radius_cu, + const ReverseUVSampler &reverse_uv_sampler_orig, + Vector<SlideCurveInfo> &r_curves_to_slide) + { + const Span<float2> surface_uv_coords = curves_orig_->surface_uv_coords(); + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const Span<int> offsets = curves_orig_->offsets(); + for (const int curve_i : curve_selection_) { + const int first_point_i = offsets[curve_i]; + const float3 old_pos_cu = self_->initial_deformed_positions_cu_[first_point_i]; + const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + /* Root point is too far away from curve center. */ + continue; + } + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + + const float2 uv = surface_uv_coords[curve_i]; + ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + /* The curve does not have a valid surface attachment. */ + found_invalid_uv_mapping_.store(true); + continue; + } + /* Compute the normal at the initial surface position. */ + const float3 normal_cu = math::normalize( + transforms_.surface_to_curves_normal * + geometry::compute_surface_point_normal( + *result.looptri, result.bary_weights, corner_normals_orig_su_)); + + r_curves_to_slide.append({curve_i, radius_falloff, normal_cu}); + } + } + + void slide_with_symmetry() + { + const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_}; + for (const SlideInfo &slide_info : self_->slide_info_) { + this->slide(slide_info.curves_to_slide, reverse_uv_sampler_orig, slide_info.brush_transform); + } + } + + void slide(const Span<SlideCurveInfo> slide_curves, + const ReverseUVSampler &reverse_uv_sampler_orig, + const float4x4 &brush_transform) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + const Span<MVert> verts_orig_su = surface_orig_->verts(); + const Span<MLoop> loops_orig = surface_orig_->loops(); + + MutableSpan<float3> positions_orig_cu = curves_orig_->positions_for_write(); + MutableSpan<float2> surface_uv_coords = curves_orig_->surface_uv_coords_for_write(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); + + const float2 brush_pos_diff_re = brush_pos_re_ - self_->initial_brush_pos_re_; + + /* The brush transformation has to be applied in curves space. */ + const float4x4 world_to_surface_with_symmetry_mat = transforms_.curves_to_surface * + brush_transform * + transforms_.world_to_curves; + + threading::parallel_for(slide_curves.index_range(), 256, [&](const IndexRange range) { + for (const SlideCurveInfo &slide_curve_info : slide_curves.slice(range)) { + const int curve_i = slide_curve_info.curve_i; + const IndexRange points = curves_orig_->points_for_curve(curve_i); + const int first_point_i = points[0]; + + const float3 old_first_pos_eval_cu = self_->initial_deformed_positions_cu_[first_point_i]; + const float3 old_first_symm_pos_eval_cu = brush_transform_inv * old_first_pos_eval_cu; + const float3 old_first_pos_eval_su = transforms_.curves_to_surface * old_first_pos_eval_cu; + + float2 old_first_symm_pos_eval_re; + ED_view3d_project_float_v2_m4(ctx_.region, + old_first_symm_pos_eval_cu, + old_first_symm_pos_eval_re, + projection.values); + + const float radius_falloff = slide_curve_info.radius_falloff; + const float curve_weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; + const float2 new_first_symm_pos_eval_re = old_first_symm_pos_eval_re + + curve_weight * brush_pos_diff_re; + + /* Compute the ray that will be used to find the new position on the surface. */ + float3 ray_start_wo, ray_end_wo; + ED_view3d_win_to_segment_clipped(ctx_.depsgraph, + ctx_.region, + ctx_.v3d, + new_first_symm_pos_eval_re, + ray_start_wo, + ray_end_wo, + true); + const float3 ray_start_su = world_to_surface_with_symmetry_mat * ray_start_wo; + const float3 ray_end_su = world_to_surface_with_symmetry_mat * ray_end_wo; + const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); + + /* Find the ray hit that is closest to the initial curve root position. */ + int looptri_index_eval; + float3 hit_pos_eval_su; + if (!this->find_closest_ray_hit(ray_start_su, + ray_direction_su, + old_first_pos_eval_su, + looptri_index_eval, + hit_pos_eval_su)) { + continue; + } + + /* Compute the uv of the new surface position on the evaluated mesh. */ + const MLoopTri &looptri_eval = surface_looptris_eval_[looptri_index_eval]; + const float3 bary_weights_eval = bke::mesh_surface_sample::compute_bary_coord_in_triangle( + surface_verts_eval_, surface_loops_eval_, looptri_eval, hit_pos_eval_su); + const float2 uv = attribute_math::mix3(bary_weights_eval, + surface_uv_map_eval_[looptri_eval.tri[0]], + surface_uv_map_eval_[looptri_eval.tri[1]], + surface_uv_map_eval_[looptri_eval.tri[2]]); + + /* Try to find the same uv on the original surface. */ + const ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + found_invalid_uv_mapping_.store(true); + continue; + } + const MLoopTri &looptri_orig = *result.looptri; + const float3 &bary_weights_orig = result.bary_weights; + + /* Gather old and new surface normal. */ + const float3 &initial_normal_cu = slide_curve_info.initial_normal_cu; + const float3 new_normal_cu = math::normalize( + transforms_.surface_to_curves_normal * + geometry::compute_surface_point_normal( + *result.looptri, result.bary_weights, corner_normals_orig_su_)); + + /* Gather old and new surface position. */ + const float3 old_first_pos_orig_cu = self_->initial_positions_cu_[first_point_i]; + const float3 new_first_pos_orig_cu = + transforms_.surface_to_curves * + attribute_math::mix3<float3>(bary_weights_orig, + verts_orig_su[loops_orig[looptri_orig.tri[0]].v].co, + verts_orig_su[loops_orig[looptri_orig.tri[1]].v].co, + verts_orig_su[loops_orig[looptri_orig.tri[2]].v].co); + + /* Actually transform curve points. */ + const float4x4 slide_transform = this->get_slide_transform( + old_first_pos_orig_cu, new_first_pos_orig_cu, initial_normal_cu, new_normal_cu); + for (const int point_i : points) { + positions_orig_cu[point_i] = slide_transform * self_->initial_positions_cu_[point_i]; + } + surface_uv_coords[curve_i] = uv; + } + }); + } + + bool find_closest_ray_hit(const float3 &ray_start_su, + const float3 &ray_direction_su, + const float3 &point_su, + int &r_looptri_index, + float3 &r_hit_pos) + { + float best_dist_sq_su = FLT_MAX; + int best_looptri_index_eval; + float3 best_hit_pos_su; + BLI_bvhtree_ray_cast_all_cpp( + *surface_bvh_eval_.tree, + ray_start_su, + ray_direction_su, + 0.0f, + FLT_MAX, + [&](const int looptri_index, const BVHTreeRay &ray, BVHTreeRayHit &hit) { + surface_bvh_eval_.raycast_callback(&surface_bvh_eval_, looptri_index, &ray, &hit); + if (hit.index < 0) { + return; + } + const float3 &hit_pos_su = hit.co; + const float dist_sq_su = math::distance_squared(hit_pos_su, point_su); + if (dist_sq_su < best_dist_sq_su) { + best_dist_sq_su = dist_sq_su; + best_hit_pos_su = hit_pos_su; + best_looptri_index_eval = hit.index; + } + }); + + if (best_dist_sq_su == FLT_MAX) { + return false; + } + r_looptri_index = best_looptri_index_eval; + r_hit_pos = best_hit_pos_su; + return true; + } + + float4x4 get_slide_transform(const float3 &old_root_pos, + const float3 &new_root_pos, + const float3 &old_normal, + const float3 &new_normal) + { + float3x3 rotation_3x3; + rotation_between_vecs_to_mat3(rotation_3x3.values, old_normal, new_normal); + float4x4 rotation_4x4; + copy_m4_m3(rotation_4x4.values, rotation_3x3.values); + + float4x4 transform = float4x4::identity(); + sub_v3_v3(transform.values[3], old_root_pos); + mul_m4_m4_pre(transform.values, rotation_4x4.values); + add_v3_v3(transform.values[3], new_root_pos); + return transform; + } +}; + +void SlideOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) +{ + SlideOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_slide_operation() +{ + return std::make_unique<SlideOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc new file mode 100644 index 00000000000..37a7f1c83ed --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc @@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_crazyspace.hh" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" + +#include "DNA_brush_types.h" + +#include "WM_api.h" + +#include "BLI_enumerable_thread_specific.hh" + +#include "curves_sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { + +class SmoothOperation : public CurvesSculptStrokeOperation { + private: + /** Only used when a 3D brush is used. */ + CurvesBrush3D brush_3d_; + + friend struct SmoothOperationExecutor; + + public: + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct SmoothOperationExecutor { + SmoothOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + VArray<float> point_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + float2 brush_pos_re_; + + CurvesSurfaceTransforms transforms_; + + SmoothOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(SmoothOperation &self, const bContext &C, const StrokeExtension &stroke_extension) + { + UNUSED_VARS(C, stroke_extension); + self_ = &self; + + object_ = CTX_data_active_object(&C); + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + brush_pos_re_ = stroke_extension.mouse_position; + + point_factors_ = get_point_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + if (stroke_extension.is_first) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); + } + } + + Array<float> point_smooth_factors(curves_->points_num(), 0.0f); + + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->find_projected_smooth_factors_with_symmetry(point_smooth_factors); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->find_spherical_smooth_factors_with_symmetry(point_smooth_factors); + } + else { + BLI_assert_unreachable(); + } + + this->smooth(point_smooth_factors); + curves_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); + } + + void find_projected_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->find_projected_smooth_factors(brush_transform, r_point_smooth_factors); + } + } + + void find_projected_smooth_factors(const float4x4 &brush_transform, + MutableSpan<float> r_point_smooth_factors) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points) { + const float3 &pos_cu = brush_transform_inv * deformation.positions[point_i]; + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + const float dist_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_re_); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re); + /* Used to make the brush easier to use. Otherwise a strength of 1 would be way too + * large. */ + const float weight_factor = 0.1f; + const float weight = weight_factor * brush_strength_ * radius_falloff * + point_factors_[point_i]; + math::max_inplace(r_point_smooth_factors[point_i], weight); + } + } + }); + } + + void find_spherical_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + float3 brush_pos_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_pos_wo); + const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->find_spherical_smooth_factors( + brush_transform * brush_pos_cu, brush_radius_cu, r_point_smooth_factors); + } + } + + void find_spherical_smooth_factors(const float3 &brush_pos_cu, + const float brush_radius_cu, + MutableSpan<float> r_point_smooth_factors) + { + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points) { + const float3 &pos_cu = deformation.positions[point_i]; + const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + /* Used to make the brush easier to use. Otherwise a strength of 1 would be way too + * large. */ + const float weight_factor = 0.1f; + const float weight = weight_factor * brush_strength_ * radius_falloff * + point_factors_[point_i]; + math::max_inplace(r_point_smooth_factors[point_i], weight); + } + } + }); + } + + void smooth(const Span<float> point_smooth_factors) + { + MutableSpan<float3> positions = curves_->positions_for_write(); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + Vector<float3> old_positions; + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + old_positions.clear(); + old_positions.extend(positions.slice(points)); + for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) { + const int point_i = points[i]; + const float smooth_factor = point_smooth_factors[point_i]; + if (smooth_factor == 0.0f) { + continue; + } + /* Move towards the middle of the neighboring points. */ + const float3 old_pos = old_positions[i]; + const float3 &prev_pos = old_positions[i - 1]; + const float3 &next_pos = old_positions[i + 1]; + const float3 goal_pos = math::midpoint(prev_pos, next_pos); + const float3 new_pos = math::interpolate(old_pos, goal_pos, smooth_factor); + positions[point_i] = new_pos; + } + } + }); + } +}; + +void SmoothOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) +{ + SmoothOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_smooth_operation() +{ + return std::make_unique<SmoothOperation>(); +} + +} // namespace blender::ed::sculpt_paint 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 e1aecabdcc7..54b81fa221d 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -89,8 +89,7 @@ struct SnakeHookOperatorExecutor { Vector<int64_t> selected_curve_indices_; IndexMask curve_selection_; - float4x4 curves_to_world_mat_; - float4x4 world_to_curves_mat_; + CurvesSurfaceTransforms transforms_; float2 brush_pos_prev_re_; float2 brush_pos_re_; @@ -118,15 +117,14 @@ struct SnakeHookOperatorExecutor { falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); - curves_to_world_mat_ = object_->obmat; - world_to_curves_mat_ = curves_to_world_mat_.inverted(); - curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); if (curves_->curves_num() == 0) { return; } + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + curve_factors_ = get_curves_selection(*curves_id_); curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); @@ -178,6 +176,8 @@ struct SnakeHookOperatorExecutor { void projected_snake_hook(const float4x4 &brush_transform) { const float4x4 brush_transform_inv = brush_transform.inverted(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); MutableSpan<float3> positions_cu = curves_->positions_for_write(); @@ -191,12 +191,14 @@ struct SnakeHookOperatorExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); - const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i]; + const float3 old_pos_cu = deformation.positions[last_point_i]; + const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; - float2 old_pos_re; - ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values); + float2 old_symm_pos_re; + ED_view3d_project_float_v2_m4( + ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values); - const float distance_to_brush_sq_re = math::distance_squared(old_pos_re, + const float distance_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_prev_re_); if (distance_to_brush_sq_re > brush_radius_sq_re) { continue; @@ -206,16 +208,21 @@ struct SnakeHookOperatorExecutor { brush_, std::sqrt(distance_to_brush_sq_re), brush_radius_re); const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; - const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; - float3 new_position_wo; + const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight; + float3 new_symm_pos_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * old_pos_cu, - new_position_re, - new_position_wo); - const float3 new_position_cu = brush_transform * (world_to_curves_mat_ * new_position_wo); - - move_last_point_and_resample(positions_cu.slice(points), new_position_cu); + transforms_.curves_to_world * old_symm_pos_cu, + new_symm_pos_re, + new_symm_pos_wo); + const float3 new_pos_cu = brush_transform * + (transforms_.world_to_curves * new_symm_pos_wo); + const float3 translation_eval = new_pos_cu - old_pos_cu; + 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); } }); } @@ -228,16 +235,16 @@ struct SnakeHookOperatorExecutor { float3 brush_start_wo, brush_end_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_prev_re_, brush_start_wo); ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - curves_to_world_mat_ * self_->brush_3d_.position_cu, + transforms_.curves_to_world * self_->brush_3d_.position_cu, brush_pos_re_, brush_end_wo); - const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; - const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + const float3 brush_start_cu = transforms_.world_to_curves * brush_start_wo; + const float3 brush_end_cu = transforms_.world_to_curves * brush_end_wo; const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; @@ -253,6 +260,9 @@ struct SnakeHookOperatorExecutor { const float3 &brush_end_cu, const float brush_radius_cu) { + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); const float3 brush_diff_cu = brush_end_cu - brush_start_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); @@ -261,7 +271,7 @@ struct SnakeHookOperatorExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); - const float3 old_pos_cu = positions_cu[last_point_i]; + const float3 old_pos_cu = deformation.positions[last_point_i]; const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3( old_pos_cu, brush_start_cu, brush_end_cu); @@ -275,9 +285,12 @@ struct SnakeHookOperatorExecutor { brush_, distance_to_brush_cu, brush_radius_cu); const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; - const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu; + const float3 translation_eval = weight * brush_diff_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + last_point_i, translation_eval); - move_last_point_and_resample(positions_cu.slice(points), new_pos_cu); + move_last_point_and_resample(positions_cu.slice(points), + positions_cu[last_point_i] + translation_orig); } }); } diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index c5ebcf870a3..164e13ac3c9 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -628,7 +628,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, /* Premultiplied alpha blending. */ GPU_blend(GPU_BLEND_ALPHA_PREMULT); - immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR); float final_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; if (!col) { @@ -720,7 +720,7 @@ static bool paint_draw_cursor_overlay( GPU_blend(GPU_BLEND_ALPHA_PREMULT); - immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR); float final_color[4] = {UNPACK3(U.sculpt_paint_overlay_col), 1.0f}; mul_v4_fl(final_color, brush->cursor_overlay_alpha * 0.01f); @@ -915,7 +915,7 @@ static void paint_draw_curve_cursor(Brush *brush, ViewContext *vc) /* Draw the bezier handles and the curve segment between the current and next point. */ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); float selec_col[4], handle_col[4], pivot_col[4]; UI_GetThemeColorType4fv(TH_VERTEX_SELECT, SPACE_VIEW3D, selec_col); @@ -1145,11 +1145,10 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, } GPU_line_width(1.0f); - if (ss->preview_vert_index_count > 0) { - immBegin(GPU_PRIM_LINES, ss->preview_vert_index_count); - for (int i = 0; i < ss->preview_vert_index_count; i++) { - immVertex3fv(gpuattr, - SCULPT_vertex_co_for_grab_active_get(ss, ss->preview_vert_index_list[i])); + if (ss->preview_vert_count > 0) { + immBegin(GPU_PRIM_LINES, ss->preview_vert_count); + for (int i = 0; i < ss->preview_vert_count; i++) { + immVertex3fv(gpuattr, SCULPT_vertex_co_for_grab_active_get(ss, ss->preview_vert_list[i])); } immEnd(); } @@ -1209,7 +1208,7 @@ typedef struct PaintCursorContext { /* Sculpt related data. */ Sculpt *sd; SculptSession *ss; - int prev_active_vertex_index; + PBVHVertRef prev_active_vertex; bool is_stroke_active; bool is_cursor_over_mesh; bool is_multires; @@ -1366,7 +1365,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon /* This updates the active vertex, which is needed for most of the Sculpt/Vertex Colors tools to * work correctly */ - pcontext->prev_active_vertex_index = ss->active_vertex_index; + pcontext->prev_active_vertex = ss->active_vertex; if (!ups->stroke_active) { pcontext->is_cursor_over_mesh = SCULPT_cursor_geometry_info_update( C, &gi, mval_fl, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); @@ -1549,7 +1548,7 @@ static void paint_cursor_preview_boundary_data_update(PaintCursorContext *pconte } ss->boundary_preview = SCULPT_boundary_data_init( - pcontext->vc.obact, pcontext->brush, ss->active_vertex_index, pcontext->radius); + pcontext->vc.obact, pcontext->brush, ss->active_vertex, pcontext->radius); } static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *pcontext) @@ -1575,8 +1574,8 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * paint_cursor_update_object_space_radius(pcontext); - const bool update_previews = pcontext->prev_active_vertex_index != - SCULPT_active_vertex_get(pcontext->ss); + const bool update_previews = pcontext->prev_active_vertex.i != + SCULPT_active_vertex_get(pcontext->ss).i; /* Setup drawing. */ wmViewport(&pcontext->region->winrct); @@ -1870,7 +1869,7 @@ static void paint_cursor_setup_2D_drawing(PaintCursorContext *pcontext) GPU_line_smooth(true); pcontext->pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); } static void paint_cursor_setup_3D_drawing(PaintCursorContext *pcontext) diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index 944b3f953a0..c1289364fb2 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -78,6 +78,12 @@ static void partialvis_update_mesh(Object *ob, BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK); + bool *hide_vert = CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"); + if (hide_vert == NULL) { + hide_vert = CustomData_add_layer_named( + &me->vdata, CD_PROP_BOOL, CD_SET_DEFAULT, NULL, me->totvert, ".hide_vert"); + } + SCULPT_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); for (i = 0; i < totvert; i++) { @@ -86,16 +92,11 @@ static void partialvis_update_mesh(Object *ob, /* Hide vertex if in the hide volume. */ if (is_effected(area, planes, v->co, vmask)) { - if (action == PARTIALVIS_HIDE) { - v->flag |= ME_HIDE; - } - else { - v->flag &= ~ME_HIDE; - } + hide_vert[vert_indices[i]] = (action == PARTIALVIS_HIDE); any_changed = true; } - if (!(v->flag & ME_HIDE)) { + if (!hide_vert[vert_indices[i]]) { any_visible = true; } } @@ -350,10 +351,10 @@ static int hide_show_exec(bContext *C, wmOperator *op) /* Start undo. */ switch (action) { case PARTIALVIS_HIDE: - SCULPT_undo_push_begin(ob, "Hide area"); + SCULPT_undo_push_begin_ex(ob, "Hide area"); break; case PARTIALVIS_SHOW: - SCULPT_undo_push_begin(ob, "Show area"); + SCULPT_undo_push_begin_ex(ob, "Show area"); break; } diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index 56cd4751881..c852fd25bc4 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -125,7 +125,7 @@ void ED_imapaint_dirty_region( imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh); - ListBase *undo_tiles = ED_image_paint_tile_list_get(); + PaintTileMap *undo_tiles = ED_image_paint_tile_map_get(); for (ty = tiley; ty <= tileh; ty++) { for (tx = tilex; tx <= tilew; tx++) { @@ -158,11 +158,21 @@ void imapaint_image_update( imapaintpartial.dirty_region.xmax, imapaintpartial.dirty_region.ymax); + /* When buffer is partial updated the planes should be set to a larger value than 8. This will + * make sure that partial updating is working but uses more GPU memory as the gpu texture will + * have 4 channels. When so the whole texture needs to be reuploaded to the GPU using the new + * texture format. */ + if (ibuf != nullptr && ibuf->planes == 8) { + ibuf->planes = 32; + BKE_image_partial_update_mark_full_update(image); + return; + } + /* TODO: should set_tpage create ->rect? */ if (texpaint || (sima && sima->lock)) { const int w = BLI_rcti_size_x(&imapaintpartial.dirty_region); const int h = BLI_rcti_size_y(&imapaintpartial.dirty_region); - /* Testing with partial update in uv editor too */ + /* Testing with partial update in uv editor too. */ BKE_image_update_gputexture( image, iuser, imapaintpartial.dirty_region.xmin, imapaintpartial.dirty_region.ymin, w, h); } @@ -276,10 +286,11 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool) (ID_IS_LINKED(sima->image) || ID_IS_OVERRIDE_LIBRARY(sima->image))) { return false; } - ARegion *region = CTX_wm_region(C); - - if ((sima->mode == SI_MODE_PAINT) && region->regiontype == RGN_TYPE_WINDOW) { - return true; + if (sima->mode == SI_MODE_PAINT) { + const ARegion *region = CTX_wm_region(C); + if (region->regiontype == RGN_TYPE_WINDOW) { + return true; + } } } } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index fae2e6863fa..5df65e596b9 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -1196,7 +1196,7 @@ static void paint_2d_do_making_brush(ImagePaintState *s, ImBuf tmpbuf; IMB_initImBuf(&tmpbuf, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, 0); - ListBase *undo_tiles = ED_image_paint_tile_list_get(); + PaintTileMap *undo_tiles = ED_image_paint_tile_map_get(); for (int ty = tiley; ty <= tileh; ty++) { for (int tx = tilex; tx <= tilew; tx++) { 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 a671c24c514..928f3e9a496 100644 --- a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc +++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc @@ -13,6 +13,7 @@ #include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_paint.h" #include "BKE_undo_system.h" @@ -252,7 +253,7 @@ static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customda ARegion *region = pop->vc.region; - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); GPU_line_width(4.0); immUniformColor4ub(0, 0, 0, 255); @@ -293,7 +294,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); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); /* initialize from context */ if (CTX_wm_region_view3d(C)) { diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 50480b8aef0..1ca5df47e17 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -56,6 +56,7 @@ #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_image.h" +#include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" @@ -414,6 +415,7 @@ typedef struct ProjPaintState { const float (*vert_normals)[3]; const MEdge *medge_eval; const MPoly *mpoly_eval; + const int *material_indices; const MLoop *mloop_eval; const MLoopTri *mlooptri_eval; @@ -542,8 +544,8 @@ static int project_paint_face_paint_tile(Image *ima, const float *uv) static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int tri_index) { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; + const int poly_i = ps->mlooptri_eval[tri_index].poly; + Material *ma = ps->mat_array[ps->material_indices == NULL ? 0 : ps->material_indices[poly_i]]; return ma ? ma->texpaintslot + ma->paint_active_slot : NULL; } @@ -553,23 +555,23 @@ static Image *project_paint_face_paint_image(const ProjPaintState *ps, int tri_i return ps->stencil_ima; } - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; + const int poly_i = ps->mlooptri_eval[tri_index].poly; + Material *ma = ps->mat_array[ps->material_indices == NULL ? 0 : ps->material_indices[poly_i]]; TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : NULL; return slot ? slot->ima : ps->canvas_ima; } static TexPaintSlot *project_paint_face_clone_slot(const ProjPaintState *ps, int tri_index) { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; + const int poly_i = ps->mlooptri_eval[tri_index].poly; + Material *ma = ps->mat_array[ps->material_indices == NULL ? 0 : ps->material_indices[poly_i]]; return ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; } static Image *project_paint_face_clone_image(const ProjPaintState *ps, int tri_index) { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; + const int poly_i = ps->mlooptri_eval[tri_index].poly; + Material *ma = ps->mat_array[ps->material_indices == NULL ? 0 : ps->material_indices[poly_i]]; TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; return slot ? slot->ima : ps->clone_ima; } @@ -1223,12 +1225,12 @@ static VertSeam *find_adjacent_seam(const ProjPaintState *ps, /* Circulate through the (sorted) vert seam array, in the direction of the seam normal, * until we find the first opposing seam, matching in UV space. */ if (seam->normal_cw) { - LISTBASE_CIRCULAR_BACKWARD_BEGIN (vert_seams, adjacent, seam) { + LISTBASE_CIRCULAR_BACKWARD_BEGIN (VertSeam *, vert_seams, adjacent, seam) { if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) { break; } } - LISTBASE_CIRCULAR_BACKWARD_END(vert_seams, adjacent, seam); + LISTBASE_CIRCULAR_BACKWARD_END(VertSeam *, vert_seams, adjacent, seam); } else { LISTBASE_CIRCULAR_FORWARD_BEGIN (vert_seams, adjacent, seam) { @@ -1813,7 +1815,7 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) } if (generate_tile) { - ListBase *undo_tiles = ED_image_paint_tile_list_get(); + struct PaintTileMap *undo_tiles = ED_image_paint_tile_map_get(); volatile void *undorect; if (tinf->masked) { undorect = ED_image_paint_tile_push(undo_tiles, @@ -4053,13 +4055,15 @@ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *p } ps->mat_array[totmat - 1] = NULL; - ps->mvert_eval = ps->me_eval->mvert; + ps->mvert_eval = BKE_mesh_verts(ps->me_eval); ps->vert_normals = BKE_mesh_vertex_normals_ensure(ps->me_eval); if (ps->do_mask_cavity) { - ps->medge_eval = ps->me_eval->medge; + ps->medge_eval = BKE_mesh_edges(ps->me_eval); } - ps->mloop_eval = ps->me_eval->mloop; - ps->mpoly_eval = ps->me_eval->mpoly; + ps->mloop_eval = BKE_mesh_loops(ps->me_eval); + ps->mpoly_eval = BKE_mesh_polys(ps->me_eval); + ps->material_indices = (const int *)CustomData_get_layer_named( + &ps->me_eval->pdata, CD_PROP_INT32, "material_index"); ps->totvert_eval = ps->me_eval->totvert; ps->totedge_eval = ps->me_eval->totedge; @@ -4151,7 +4155,7 @@ static void proj_paint_face_lookup_init(const ProjPaintState *ps, ProjPaintFaceL memset(face_lookup, 0, sizeof(*face_lookup)); if (ps->do_face_sel) { face_lookup->index_mp_to_orig = CustomData_get_layer(&ps->me_eval->pdata, CD_ORIGINDEX); - face_lookup->mpoly_orig = ((Mesh *)ps->ob->data)->mpoly; + face_lookup->mpoly_orig = BKE_mesh_polys((Mesh *)ps->ob->data); } } @@ -6038,7 +6042,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) int orig_brush_size; IDProperty *idgroup; IDProperty *view_data = NULL; - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); bool uvs, mat, tex; if (ob == NULL || ob->type != OB_MESH) { diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 3f4e660b229..99c25953d50 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -46,7 +46,10 @@ typedef struct CoNo { /* paint_stroke.c */ -typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]); +typedef bool (*StrokeGetLocation)(struct bContext *C, + float location[3], + const float mouse[2], + bool force_original); typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]); typedef void (*StrokeUpdateStep)(struct bContext *C, struct wmOperator *op, @@ -134,18 +137,10 @@ void PAINT_OT_vertex_paint(struct wmOperatorType *ot); unsigned int vpaint_get_current_color(struct Scene *scene, struct VPaint *vp, bool secondary); -/* paint_vertex_color_utils.c */ - /** * \note weight-paint has an equivalent function: #ED_wpaint_blend_tool */ unsigned int ED_vpaint_blend_tool(int tool, uint col, uint paintcol, int alpha_i); -/** - * Apply callback to each vertex of the active vertex color layer. - */ -bool ED_vpaint_color_transform(struct Object *ob, - VPaintTransform_Callback vpaint_tx_fn, - const void *user_data); /* paint_vertex_weight_utils.c */ @@ -378,10 +373,12 @@ void PAINT_OT_face_select_linked(struct wmOperatorType *ot); void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot); void PAINT_OT_face_select_all(struct wmOperatorType *ot); void PAINT_OT_face_select_hide(struct wmOperatorType *ot); -void PAINT_OT_face_select_reveal(struct wmOperatorType *ot); + +void PAINT_OT_face_vert_reveal(struct wmOperatorType *ot); void PAINT_OT_vert_select_all(struct wmOperatorType *ot); void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot); +void PAINT_OT_vert_select_hide(struct wmOperatorType *ot); bool vert_paint_poll(struct bContext *C); bool mask_paint_poll(struct bContext *C); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 89bbf2a3c92..332a0830081 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -152,7 +152,7 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); - SCULPT_undo_push_begin(ob, "Mask flood fill"); + SCULPT_undo_push_begin(ob, op); MaskTaskData data = { .ob = ob, @@ -663,7 +663,7 @@ static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, co static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd) { float vertex_normal[3]; - SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal); + SCULPT_vertex_normal_get(sgcontext->ss, vd->vertex, vertex_normal); float dot = dot_v3v3(sgcontext->view_normal, vertex_normal); const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f); @@ -687,10 +687,10 @@ static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, P return false; } -static void sculpt_gesture_apply(bContext *C, SculptGestureContext *sgcontext) +static void sculpt_gesture_apply(bContext *C, SculptGestureContext *sgcontext, wmOperator *op) { SculptGestureOperation *operation = sgcontext->operation; - SCULPT_undo_push_begin(CTX_data_active_object(C), "Sculpt Gesture Apply"); + SCULPT_undo_push_begin(CTX_data_active_object(C), op); operation->sculpt_gesture_begin(C, sgcontext); @@ -743,7 +743,7 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { - SCULPT_vertex_face_set_set(sgcontext->ss, vd.index, face_set_operation->new_face_set_id); + SCULPT_vertex_face_set_set(sgcontext->ss, vd.vertex, face_set_operation->new_face_set_id); any_updated = true; } } @@ -1025,7 +1025,9 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext) trim_operation->depth_back = -FLT_MAX; for (int i = 0; i < totvert; i++) { - const float *vco = SCULPT_vertex_co_get(ss, i); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + const float *vco = SCULPT_vertex_co_get(ss, vertex); /* Convert the coordinates to world space to calculate the depth. When generating the trimming * mesh, coordinates are first calculated in world space, then converted to object space to * store them. */ @@ -1121,6 +1123,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex const float(*ob_imat)[4] = vc->obact->imat; /* Write vertices coordinates for the front face. */ + MVert *verts = BKE_mesh_verts_for_write(trim_operation->mesh); float depth_point[3]; madd_v3_v3v3fl(depth_point, shape_origin, shape_normal, depth_front); for (int i = 0; i < tot_screen_points; i++) { @@ -1132,7 +1135,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex ED_view3d_win_to_3d_on_plane(region, shape_plane, screen_points[i], false, new_point); madd_v3_v3fl(new_point, shape_normal, depth_front); } - mul_v3_m4v3(trim_operation->mesh->mvert[i].co, ob_imat, new_point); + mul_v3_m4v3(verts[i].co, ob_imat, new_point); mul_v3_m4v3(trim_operation->true_mesh_co[i], ob_imat, new_point); } @@ -1147,7 +1150,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex ED_view3d_win_to_3d_on_plane(region, shape_plane, screen_points[i], false, new_point); madd_v3_v3fl(new_point, shape_normal, depth_back); } - mul_v3_m4v3(trim_operation->mesh->mvert[i + tot_screen_points].co, ob_imat, new_point); + mul_v3_m4v3(verts[i + tot_screen_points].co, ob_imat, new_point); mul_v3_m4v3(trim_operation->true_mesh_co[i + tot_screen_points], ob_imat, new_point); } @@ -1157,10 +1160,12 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex BLI_polyfill_calc(screen_points, tot_screen_points, 0, r_tris); /* Write the front face triangle indices. */ - MPoly *mp = trim_operation->mesh->mpoly; - MLoop *ml = trim_operation->mesh->mloop; + MPoly *polys = BKE_mesh_polys_for_write(trim_operation->mesh); + MLoop *loops = BKE_mesh_loops_for_write(trim_operation->mesh); + MPoly *mp = polys; + MLoop *ml = loops; for (int i = 0; i < tot_tris_face; i++, mp++, ml += 3) { - mp->loopstart = (int)(ml - trim_operation->mesh->mloop); + mp->loopstart = (int)(ml - loops); mp->totloop = 3; ml[0].v = r_tris[i][0]; ml[1].v = r_tris[i][1]; @@ -1169,7 +1174,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex /* Write the back face triangle indices. */ for (int i = 0; i < tot_tris_face; i++, mp++, ml += 3) { - mp->loopstart = (int)(ml - trim_operation->mesh->mloop); + mp->loopstart = (int)(ml - loops); mp->totloop = 3; ml[0].v = r_tris[i][0] + tot_screen_points; ml[1].v = r_tris[i][1] + tot_screen_points; @@ -1180,7 +1185,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex /* Write the indices for the lateral triangles. */ for (int i = 0; i < tot_screen_points; i++, mp++, ml += 3) { - mp->loopstart = (int)(ml - trim_operation->mesh->mloop); + mp->loopstart = (int)(ml - loops); mp->totloop = 3; int current_index = i; int next_index = current_index + 1; @@ -1193,7 +1198,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex } for (int i = 0; i < tot_screen_points; i++, mp++, ml += 3) { - mp->loopstart = (int)(ml - trim_operation->mesh->mloop); + mp->loopstart = (int)(ml - loops); mp->totloop = 3; int current_index = i; int next_index = current_index + 1; @@ -1328,8 +1333,9 @@ static void sculpt_gesture_trim_apply_for_symmetry_pass(bContext *UNUSED(C), { SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)sgcontext->operation; Mesh *trim_mesh = trim_operation->mesh; + MVert *verts = BKE_mesh_verts_for_write(trim_mesh); for (int i = 0; i < trim_mesh->totvert; i++) { - flip_v3_v3(trim_mesh->mvert[i].co, trim_operation->true_mesh_co[i], sgcontext->symmpass); + flip_v3_v3(verts[i].co, trim_operation->true_mesh_co[i], sgcontext->symmpass); } sculpt_gesture_trim_normals_update(sgcontext); sculpt_gesture_apply_trim(sgcontext); @@ -1437,7 +1443,7 @@ static void project_line_gesture_apply_task_cb(void *__restrict userdata, } add_v3_v3(vd.co, disp); if (vd.mvert) { - BKE_pbvh_vert_mark_update(sgcontext->ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(sgcontext->ss->pbvh, vd.vertex); } any_updated = true; } @@ -1500,7 +1506,7 @@ static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } sculpt_gesture_init_mask_properties(sgcontext, op); - sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; } @@ -1512,7 +1518,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } sculpt_gesture_init_mask_properties(sgcontext, op); - sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; } @@ -1524,7 +1530,7 @@ static int paint_mask_gesture_line_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } sculpt_gesture_init_mask_properties(sgcontext, op); - sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; } @@ -1536,7 +1542,7 @@ static int face_set_gesture_box_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } sculpt_gesture_init_face_set_properties(sgcontext, op); - sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; } @@ -1548,7 +1554,7 @@ static int face_set_gesture_lasso_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } sculpt_gesture_init_face_set_properties(sgcontext, op); - sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; } @@ -1573,7 +1579,7 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op) } sculpt_gesture_init_trim_properties(sgcontext, op); - sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; } @@ -1614,7 +1620,7 @@ static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } sculpt_gesture_init_trim_properties(sgcontext, op); - sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; } @@ -1643,7 +1649,7 @@ static int project_gesture_line_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } sculpt_gesture_init_project_properties(sgcontext, op); - sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_apply(C, sgcontext, op); sculpt_gesture_context_free(sgcontext); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 0f2b02ed3ab..b78c60e7964 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -90,13 +90,13 @@ static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool, if (STREQ(tool->runtime->data_block, "DRAW")) { return GP_BRUSH_PRESET_PENCIL; } - else if (STREQ(tool->runtime->data_block, "FILL")) { + if (STREQ(tool->runtime->data_block, "FILL")) { return GP_BRUSH_PRESET_FILL_AREA; } - else if (STREQ(tool->runtime->data_block, "ERASE")) { + if (STREQ(tool->runtime->data_block, "ERASE")) { return GP_BRUSH_PRESET_ERASER_SOFT; } - else if (STREQ(tool->runtime->data_block, "TINT")) { + if (STREQ(tool->runtime->data_block, "TINT")) { return GP_BRUSH_PRESET_TINT; } break; @@ -105,28 +105,28 @@ static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool, if (STREQ(tool->runtime->data_block, "SMOOTH")) { return GP_BRUSH_PRESET_SMOOTH_STROKE; } - else if (STREQ(tool->runtime->data_block, "STRENGTH")) { + if (STREQ(tool->runtime->data_block, "STRENGTH")) { return GP_BRUSH_PRESET_STRENGTH_STROKE; } - else if (STREQ(tool->runtime->data_block, "THICKNESS")) { + if (STREQ(tool->runtime->data_block, "THICKNESS")) { return GP_BRUSH_PRESET_THICKNESS_STROKE; } - else if (STREQ(tool->runtime->data_block, "GRAB")) { + if (STREQ(tool->runtime->data_block, "GRAB")) { return GP_BRUSH_PRESET_GRAB_STROKE; } - else if (STREQ(tool->runtime->data_block, "PUSH")) { + if (STREQ(tool->runtime->data_block, "PUSH")) { return GP_BRUSH_PRESET_PUSH_STROKE; } - else if (STREQ(tool->runtime->data_block, "TWIST")) { + if (STREQ(tool->runtime->data_block, "TWIST")) { return GP_BRUSH_PRESET_TWIST_STROKE; } - else if (STREQ(tool->runtime->data_block, "PINCH")) { + if (STREQ(tool->runtime->data_block, "PINCH")) { return GP_BRUSH_PRESET_PINCH_STROKE; } - else if (STREQ(tool->runtime->data_block, "RANDOMIZE")) { + if (STREQ(tool->runtime->data_block, "RANDOMIZE")) { return GP_BRUSH_PRESET_RANDOMIZE_STROKE; } - else if (STREQ(tool->runtime->data_block, "CLONE")) { + if (STREQ(tool->runtime->data_block, "CLONE")) { return GP_BRUSH_PRESET_CLONE_STROKE; } break; @@ -138,23 +138,22 @@ static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool, if (STREQ(tool->runtime->data_block, "DRAW")) { return GP_BRUSH_PRESET_VERTEX_DRAW; } - else if (STREQ(tool->runtime->data_block, "BLUR")) { + if (STREQ(tool->runtime->data_block, "BLUR")) { return GP_BRUSH_PRESET_VERTEX_BLUR; } - else if (STREQ(tool->runtime->data_block, "AVERAGE")) { + if (STREQ(tool->runtime->data_block, "AVERAGE")) { return GP_BRUSH_PRESET_VERTEX_AVERAGE; } - else if (STREQ(tool->runtime->data_block, "SMEAR")) { + if (STREQ(tool->runtime->data_block, "SMEAR")) { return GP_BRUSH_PRESET_VERTEX_SMEAR; } - else if (STREQ(tool->runtime->data_block, "REPLACE")) { + if (STREQ(tool->runtime->data_block, "REPLACE")) { return GP_BRUSH_PRESET_VERTEX_REPLACE; } break; } default: return GP_BRUSH_PRESET_UNKNOWN; - break; } return GP_BRUSH_PRESET_UNKNOWN; } @@ -362,7 +361,6 @@ static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = paint->brush; ePaintMode mode = BKE_paintmode_get_active_from_context(C); Palette *palette = paint->palette; PaletteColor *color; @@ -370,17 +368,20 @@ static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op)) color = BKE_palette_color_add(palette); palette->active_color = BLI_listbase_count(&palette->colors) - 1; - if (ELEM(mode, - PAINT_MODE_TEXTURE_3D, - PAINT_MODE_TEXTURE_2D, - PAINT_MODE_VERTEX, - PAINT_MODE_SCULPT)) { - copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush)); - color->value = 0.0; - } - else if (mode == PAINT_MODE_WEIGHT) { - zero_v3(color->rgb); - color->value = brush->weight; + if (paint->brush) { + const Brush *brush = paint->brush; + if (ELEM(mode, + PAINT_MODE_TEXTURE_3D, + PAINT_MODE_TEXTURE_2D, + PAINT_MODE_VERTEX, + PAINT_MODE_SCULPT)) { + copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush)); + color->value = 0.0; + } + else if (mode == PAINT_MODE_WEIGHT) { + zero_v3(color->rgb); + color->value = brush->weight; + } } return OPERATOR_FINISHED; @@ -1455,6 +1456,7 @@ void ED_operatortypes_paint(void) /* vertex selection */ WM_operatortype_append(PAINT_OT_vert_select_all); WM_operatortype_append(PAINT_OT_vert_select_ungrouped); + WM_operatortype_append(PAINT_OT_vert_select_hide); /* vertex */ WM_operatortype_append(PAINT_OT_vertex_paint_toggle); @@ -1473,7 +1475,8 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_face_select_linked_pick); WM_operatortype_append(PAINT_OT_face_select_all); WM_operatortype_append(PAINT_OT_face_select_hide); - WM_operatortype_append(PAINT_OT_face_select_reveal); + + WM_operatortype_append(PAINT_OT_face_vert_reveal); /* partial visibility */ WM_operatortype_append(PAINT_OT_hide_show); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 63e6dc7e965..60f4a9d59a5 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -79,6 +79,8 @@ typedef struct PaintStroke { float last_mouse_position[2]; float last_world_space_position[3]; + float last_scene_spacing_delta[3]; + bool stroke_over_mesh; /* space distance covered so far */ float stroke_distance; @@ -120,6 +122,8 @@ typedef struct PaintStroke { StrokeUpdateStep update_step; StrokeRedraw redraw; StrokeDone done; + + bool original; /* Ray-cast original mesh at start of stroke. */ } PaintStroke; /*** Cursors ***/ @@ -136,7 +140,7 @@ static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata ARegion *region = stroke->vc.region; uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4ubv(paint->paint_cursor_col); immBegin(GPU_PRIM_LINES, 2); @@ -164,7 +168,7 @@ static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata) uint shdr_pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); float viewport_size[4]; GPU_viewport_size_get_f(viewport_size); @@ -243,6 +247,11 @@ static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode) return false; } +static bool paint_tool_raycast_original(Brush *brush, ePaintMode UNUSED(mode)) +{ + return brush->flag & BRUSH_ANCHORED; +} + static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) { if (brush->flag & BRUSH_ANCHORED) { @@ -392,7 +401,7 @@ static bool paint_brush_update(bContext *C, halfway[1] = dy * 0.5f + stroke->initial_mouse[1]; if (stroke->get_location) { - if (stroke->get_location(C, r_location, halfway)) { + if (stroke->get_location(C, r_location, halfway, stroke->original)) { hit = true; location_sampled = true; location_success = true; @@ -466,7 +475,7 @@ static bool paint_brush_update(bContext *C, if (!location_sampled) { if (stroke->get_location) { - if (stroke->get_location(C, r_location, mouse)) { + if (stroke->get_location(C, r_location, mouse, stroke->original)) { location_success = true; *r_location_is_set = true; } @@ -550,8 +559,16 @@ static void paint_brush_stroke_add_step( stroke->last_pressure = pressure; if (paint_stroke_use_scene_spacing(brush, mode)) { - SCULPT_stroke_get_location(C, stroke->last_world_space_position, stroke->last_mouse_position); - mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + float world_space_position[3]; + + 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); + } + else { + add_v3_v3(stroke->last_world_space_position, stroke->last_scene_spacing_delta); + } } if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) { @@ -698,7 +715,7 @@ static float paint_space_stroke_spacing(bContext *C, spacing *= stroke->zoom_2d; if (paint_stroke_use_scene_spacing(brush, mode)) { - return max_ff(0.001f, size_clamp * spacing / 50.0f); + return size_clamp * spacing / 50.0f; } return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f); } @@ -807,7 +824,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); + bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, world_space_position); if (hit && stroke->stroke_over_mesh) { sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position); @@ -838,6 +855,8 @@ static int paint_space_stroke(bContext *C, stroke->last_world_space_position, final_world_space_position); ED_view3d_project_v2(region, final_world_space_position, mouse); + + mul_v3_v3fl(stroke->last_scene_spacing_delta, d_world_space_position, spacing); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; @@ -896,6 +915,8 @@ PaintStroke *paint_stroke_new(bContext *C, stroke->ups = ups; stroke->stroke_mode = RNA_enum_get(op->ptr, "mode"); + stroke->original = paint_tool_raycast_original(br, BKE_paintmode_get_active_from_context(C)); + get_imapaint_zoom(C, &zoomx, &zoomy); stroke->zoom_2d = max_ff(zoomx, zoomy); @@ -995,7 +1016,7 @@ static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke) static bool curves_sculpt_brush_uses_spacing(const eBrushCurvesSculptTool tool) { - return ELEM(tool, CURVES_SCULPT_TOOL_ADD); + return ELEM(tool, CURVES_SCULPT_TOOL_ADD, CURVES_SCULPT_TOOL_DENSITY); } bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) @@ -1191,8 +1212,10 @@ static void paint_line_strokes_spacing(bContext *C, copy_v2_v2(stroke->last_mouse_position, old_pos); if (use_scene_spacing) { - bool hit_old = SCULPT_stroke_get_location(C, world_space_position_old, old_pos); - bool hit_new = SCULPT_stroke_get_location(C, world_space_position_new, new_pos); + bool hit_old = SCULPT_stroke_get_location( + 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); if (hit_old && hit_new && stroke->stroke_over_mesh) { @@ -1336,7 +1359,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); + C, stroke->last_world_space_position, data + 2 * j, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); } @@ -1468,7 +1491,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); 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); + C, stroke->last_world_space_position, sample_average.mouse, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); } stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); @@ -1527,8 +1550,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS copy_v2_fl2(mouse, event->mval[0], event->mval[1]); paint_stroke_line_constrain(stroke, mouse); - if (stroke->stroke_started && - (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) { + if (stroke->stroke_started && (first_modal || ISMOUSE_MOTION(event->type))) { if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || (br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); @@ -1538,7 +1560,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS } else if (first_modal || /* regular dabs */ - (!(br->flag & BRUSH_AIRBRUSH) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) || + (!(br->flag & BRUSH_AIRBRUSH) && ISMOUSE_MOTION(event->type)) || /* airbrush */ ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER && event->customdata == stroke->timer)) { diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 3f26f590b70..1cedcde7035 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -28,7 +28,9 @@ #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_image.h" +#include "BKE_layer.h" #include "BKE_material.h" +#include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" #include "BKE_report.h" @@ -286,9 +288,8 @@ static void imapaint_pick_uv( const MLoopTri *lt = BKE_mesh_runtime_looptri_ensure(me_eval); const int tottri = me_eval->runtime.looptris.len; - const MVert *mvert = me_eval->mvert; - const MPoly *mpoly = me_eval->mpoly; - const MLoop *mloop = me_eval->mloop; + const MVert *mvert = BKE_mesh_verts(me_eval); + const MLoop *mloop = BKE_mesh_loops(me_eval); const int *index_mp_to_orig = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); /* get the needed opengl matrices */ @@ -302,6 +303,9 @@ static void imapaint_pick_uv( minabsw = 1e10; uv[0] = uv[1] = 0.0; + const int *material_indices = (const int *)CustomData_get_layer_named( + &me_eval->pdata, CD_PROP_INT32, "material_index"); + /* test all faces in the derivedmesh with the original index of the picked face */ /* face means poly here, not triangle, indeed */ for (i = 0; i < tottri; i++, lt++) { @@ -309,7 +313,6 @@ static void imapaint_pick_uv( if (findex == faceindex) { const MLoopUV *mloopuv; - const MPoly *mp = &mpoly[lt->poly]; const MLoopUV *tri_uv[3]; float tri_co[3][3]; @@ -321,7 +324,8 @@ static void imapaint_pick_uv( const Material *ma; const TexPaintSlot *slot; - ma = BKE_object_material_get(ob_eval, mp->mat_nr + 1); + ma = BKE_object_material_get( + ob_eval, material_indices == NULL ? 1 : material_indices[lt->poly] + 1); slot = &ma->texpaintslot[ma->paint_active_slot]; if (!(slot && slot->uvname && @@ -400,7 +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); - Object *ob = OBACT(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; bool use_material = (imapaint->mode == IMAGEPAINT_MODE_MATERIAL); @@ -410,6 +414,8 @@ void paint_sample_color( cddata_masks.pmask |= CD_MASK_ORIGINDEX; Mesh *me = (Mesh *)ob->data; Mesh *me_eval = mesh_get_eval_final(depsgraph, scene, ob_eval, &cddata_masks); + const int *material_indices = (const int *)CustomData_get_layer_named( + &me_eval->pdata, CD_PROP_INT32, "material_index"); ViewContext vc; const int mval[2] = {x, y}; @@ -427,8 +433,8 @@ void paint_sample_color( if (use_material) { /* Image and texture interpolation from material. */ - MPoly *mp = me_eval->mpoly + faceindex; - Material *ma = BKE_object_material_get(ob_eval, mp->mat_nr + 1); + Material *ma = BKE_object_material_get( + ob_eval, material_indices ? material_indices[faceindex] + 1 : 1); /* Force refresh since paint slots are not updated when changing interpolation. */ BKE_texpaint_slot_refresh_cache(scene, ma, ob); @@ -697,7 +703,7 @@ static int vert_select_ungrouped_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); Mesh *me = ob->data; - if (BLI_listbase_is_empty(&me->vertex_group_names) || (me->dvert == NULL)) { + if (BLI_listbase_is_empty(&me->vertex_group_names) || (BKE_mesh_deform_verts(me) == NULL)) { BKE_report(op->reports, RPT_ERROR, "No weights/vertex groups on object"); return OPERATOR_CANCELLED; } @@ -749,25 +755,68 @@ void PAINT_OT_face_select_hide(wmOperatorType *ot) ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects"); } -static int face_select_reveal_exec(bContext *C, wmOperator *op) +static int vert_select_hide_exec(bContext *C, wmOperator *op) +{ + const bool unselected = RNA_boolean_get(op->ptr, "unselected"); + Object *ob = CTX_data_active_object(C); + paintvert_hide(C, ob, unselected); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; +} + +void PAINT_OT_vert_select_hide(wmOperatorType *ot) +{ + ot->name = "Vertex Select Hide"; + ot->description = "Hide selected vertices"; + ot->idname = "PAINT_OT_vert_select_hide"; + + ot->exec = vert_select_hide_exec; + ot->poll = vert_paint_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected vertices"); +} + +static int face_vert_reveal_exec(bContext *C, wmOperator *op) { const bool select = RNA_boolean_get(op->ptr, "select"); Object *ob = CTX_data_active_object(C); - paintface_reveal(C, ob, select); + + if (BKE_paint_select_vert_test(ob)) { + paintvert_reveal(C, ob, select); + } + else { + paintface_reveal(C, ob, select); + } + ED_region_tag_redraw(CTX_wm_region(C)); return OPERATOR_FINISHED; } -void PAINT_OT_face_select_reveal(wmOperatorType *ot) +static bool face_vert_reveal_poll(bContext *C) { - ot->name = "Face Select Reveal"; - ot->description = "Reveal hidden faces"; - ot->idname = "PAINT_OT_face_select_reveal"; + Object *ob = CTX_data_active_object(C); - ot->exec = face_select_reveal_exec; - ot->poll = facemask_paint_poll; + /* Allow using this operator when no selection is enabled but hiding is applied. */ + return BKE_paint_select_elem_test(ob) || BKE_paint_always_hide_test(ob); +} + +void PAINT_OT_face_vert_reveal(wmOperatorType *ot) +{ + ot->name = "Reveal Faces/Vertices"; + ot->description = "Reveal hidden faces and vertices"; + ot->idname = "PAINT_OT_face_vert_reveal"; + + ot->exec = face_vert_reveal_exec; + ot->poll = face_vert_reveal_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "select", true, "Select", ""); + RNA_def_boolean(ot->srna, + "select", + true, + "Select", + "Specifies whether the newly revealed geometry should be selected"); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 67b60acc667..c38a79cb6bb 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -36,6 +36,7 @@ #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" @@ -227,22 +228,6 @@ static MDeformVert *defweight_prev_init(MDeformVert *dvert_prev, return dv_prev; } -/* check if we can do partial updates and have them draw realtime - * (without evaluating modifiers) */ -static bool vertex_paint_use_fast_update_check(Object *ob) -{ - const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob); - - if (me_eval != nullptr) { - const Mesh *me = BKE_mesh_from_object(ob); - if (me && me->mloopcol) { - return (me->mloopcol == CustomData_get_layer(&me_eval->ldata, CD_PROP_BYTE_COLOR)); - } - } - - return false; -} - static void paint_last_stroke_update(Scene *scene, const float location[3]) { UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; @@ -788,6 +773,8 @@ struct WeightPaintGroupData { * paint stroke update - campbell */ struct WeightPaintInfo { + MutableSpan<MDeformVert> dvert; + int defbase_tot; /* both must add up to 'defbase_tot' */ @@ -830,7 +817,7 @@ static void do_weight_paint_vertex_single( float paintweight) { Mesh *me = (Mesh *)ob->data; - MDeformVert *dv = &me->dvert[index]; + MDeformVert *dv = &wpi->dvert[index]; bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; MDeformWeight *dw; @@ -890,7 +877,7 @@ static void do_weight_paint_vertex_single( /* get the mirror def vars */ if (index_mirr != -1) { - dv_mirr = &me->dvert[index_mirr]; + dv_mirr = &wpi->dvert[index_mirr]; if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { dw_mirr = BKE_defvert_find_index(dv_mirr, vgroup_mirr); @@ -930,9 +917,9 @@ static void do_weight_paint_vertex_single( if (!brush_use_accumulate(wp)) { MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev; - MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index); + MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi->dvert.data(), index); if (index_mirr != -1) { - defweight_prev_init(dvert_prev, me->dvert, index_mirr); + defweight_prev_init(dvert_prev, wpi->dvert.data(), index_mirr); } weight_prev = BKE_defvert_find_weight(dv_prev, wpi->active.index); @@ -1043,7 +1030,7 @@ static void do_weight_paint_vertex_multi( float paintweight) { Mesh *me = (Mesh *)ob->data; - MDeformVert *dv = &me->dvert[index]; + MDeformVert *dv = &wpi->dvert[index]; bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; /* mirror vars */ @@ -1059,7 +1046,7 @@ static void do_weight_paint_vertex_multi( index_mirr = mesh_get_x_mirror_vert(ob, nullptr, index, topology); if (!ELEM(index_mirr, -1, index)) { - dv_mirr = &me->dvert[index_mirr]; + dv_mirr = &wpi->dvert[index_mirr]; } else { index_mirr = -1; @@ -1086,9 +1073,9 @@ static void do_weight_paint_vertex_multi( if (!brush_use_accumulate(wp)) { MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev; - MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index); + MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi->dvert.data(), index); if (index_mirr != -1) { - defweight_prev_init(dvert_prev, me->dvert, index_mirr); + defweight_prev_init(dvert_prev, wpi->dvert.data(), index_mirr); } oldw = BKE_defvert_multipaint_collective_weight( @@ -1191,12 +1178,12 @@ static void vertex_paint_init_session(Depsgraph *depsgraph, BLI_assert(ob->sculpt == nullptr); ob->sculpt = (SculptSession *)MEM_callocN(sizeof(SculptSession), "sculpt session"); ob->sculpt->mode_type = object_mode; - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, true); } static void vwpaint_init_stroke(Depsgraph *depsgraph, Object *ob) { - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, true); SculptSession *ss = ob->sculpt; /* Ensure ss->cache is allocated. It will mostly be initialized in @@ -1250,6 +1237,8 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) } Mesh *me = (Mesh *)ob->data; + const Span<MPoly> polys = me->polys(); + const Span<MLoop> loops = me->loops(); if (gmap->vert_to_loop == nullptr) { gmap->vert_map_mem = nullptr; @@ -1258,15 +1247,15 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) gmap->vert_to_poly = nullptr; BKE_mesh_vert_loop_map_create(&gmap->vert_to_loop, &gmap->vert_map_mem, - me->mpoly, - me->mloop, + polys.data(), + loops.data(), me->totvert, me->totpoly, me->totloop); BKE_mesh_vert_poly_map_create(&gmap->vert_to_poly, &gmap->poly_map_mem, - me->mpoly, - me->mloop, + polys.data(), + loops.data(), me->totvert, me->totpoly, me->totloop); @@ -1804,7 +1793,8 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo /* set up auto-normalize, and generate map for detecting which * vgroups affect deform bones */ wpd->lock_flags = BKE_object_defgroup_lock_flags_get(ob, wpd->defbase_tot); - if (ts->auto_normalize || ts->multipaint || wpd->lock_flags || ts->wpaint_lock_relative) { + if (ts->auto_normalize || ts->multipaint || wpd->lock_flags != nullptr || + ts->wpaint_lock_relative) { wpd->vgroup_validmap = BKE_object_defgroup_validmap_get(ob, wpd->defbase_tot); } @@ -1914,7 +1904,7 @@ static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata, const TaskParallelTLS *__restrict UNUSED(tls)) { SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; - const MDeformVert *dv = &data->me->dvert[n]; + const MDeformVert *dv = &data->wpi->dvert[n]; data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi); } @@ -1975,10 +1965,9 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : - vd.vert_indices[vd.i]; + 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 = data->me->mvert[v_index].flag; + const char v_flag = ss->mvert[v_index].flag; /* If the vertex is selected */ if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { /* Get the average poly weight */ @@ -1986,12 +1975,12 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, float weight_final = 0.0f; for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const MPoly *mp = &data->me->mpoly[p_index]; + const MPoly *mp = &ss->mpoly[p_index]; total_hit_loops += mp->totloop; for (int k = 0; k < mp->totloop; k++) { const int l_index = mp->loopstart + k; - const MLoop *ml = &data->me->mloop[l_index]; + const MLoop *ml = &ss->mloop[l_index]; weight_final += data->wpd->precomputed_weight[ml->v]; } } @@ -2071,10 +2060,9 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : - vd.vert_indices[vd.i]; + 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_curr = &data->me->mvert[v_index]; + const MVert *mv_curr = &ss->mvert[v_index]; /* If the vertex is selected */ if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) { @@ -2096,12 +2084,12 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, float weight_final = 0.0; for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const MPoly *mp = &data->me->mpoly[p_index]; - const MLoop *ml_other = &data->me->mloop[mp->loopstart]; + const MPoly *mp = &ss->mpoly[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; if (v_other_index != v_index) { - const MVert *mv_other = &data->me->mvert[v_other_index]; + const MVert *mv_other = &ss->mvert[v_other_index]; /* Get the direction from the selected vert to the neighbor. */ float other_dir[3]; @@ -2178,11 +2166,10 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, /* NOTE: grids are 1:1 with corners (aka loops). * For multires, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : - vd.vert_indices[vd.i]; + 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 = data->me->mvert[v_index].flag; + const char v_flag = ss->mvert[v_index].flag; /* If the vertex is selected */ if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { float brush_strength = cache->bstrength; @@ -2246,13 +2233,12 @@ static void do_wpaint_brush_calc_average_weight_cb_ex( 1.0f; if (angle_cos > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : - vd.vert_indices[vd.i]; - const char v_flag = data->me->mvert[v_index].flag; + 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) { - const MDeformVert *dv = &data->me->dvert[v_index]; + const MDeformVert *dv = &data->wpi->dvert[v_index]; accum->len += 1; accum->value += wpaint_get_active_weight(dv, data->wpi); } @@ -2524,7 +2510,11 @@ static void wpaint_stroke_update_step(bContext *C, /* load projection matrix */ mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); + Mesh *mesh = static_cast<Mesh *>(ob->data); + /* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */ + wpi.dvert = mesh->deform_verts_for_write(); + wpi.defbase_tot = wpd->defbase_tot; wpi.defbase_sel = wpd->defbase_sel; wpi.defbase_tot_sel = wpd->defbase_tot_sel; @@ -2546,7 +2536,7 @@ static void wpaint_stroke_update_step(bContext *C, /* *** done setting up WeightPaintInfo *** */ if (wpd->precomputed_weight) { - precompute_weight_values(C, ob, brush, wpd, &wpi, (Mesh *)ob->data); + precompute_weight_values(C, ob, brush, wpd, &wpi, mesh); } wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi); @@ -2559,9 +2549,9 @@ static void wpaint_stroke_update_step(bContext *C, mul_v3_m4v3(loc_world, ob->obmat, ss->cache->true_location); paint_last_stroke_update(scene, loc_world); - BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); - DEG_id_tag_update((ID *)ob->data, 0); + DEG_id_tag_update(&mesh->id, 0); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); swap_m4m4(wpd->vc.rv3d->persmat, mat); @@ -2593,31 +2583,14 @@ static void wpaint_stroke_done(const bContext *C, PaintStroke *stroke) WPaintData *wpd = (WPaintData *)paint_stroke_mode_data(stroke); if (wpd) { - if (wpd->defbase_sel) { - MEM_freeN((void *)wpd->defbase_sel); - } - if (wpd->vgroup_validmap) { - MEM_freeN((void *)wpd->vgroup_validmap); - } - if (wpd->vgroup_locked) { - MEM_freeN((void *)wpd->vgroup_locked); - } - if (wpd->vgroup_unlocked) { - MEM_freeN((void *)wpd->vgroup_unlocked); - } - if (wpd->lock_flags) { - MEM_freeN((void *)wpd->lock_flags); - } - if (wpd->active.lock) { - MEM_freeN((void *)wpd->active.lock); - } - if (wpd->mirror.lock) { - MEM_freeN((void *)wpd->mirror.lock); - } - if (wpd->precomputed_weight) { - MEM_freeN(wpd->precomputed_weight); - } - + MEM_SAFE_FREE(wpd->defbase_sel); + MEM_SAFE_FREE(wpd->vgroup_validmap); + MEM_SAFE_FREE(wpd->vgroup_locked); + MEM_SAFE_FREE(wpd->vgroup_unlocked); + MEM_SAFE_FREE(wpd->lock_flags); + MEM_SAFE_FREE(wpd->active.lock); + MEM_SAFE_FREE(wpd->mirror.lock); + MEM_SAFE_FREE(wpd->precomputed_weight); MEM_freeN(wpd); } @@ -2838,16 +2811,6 @@ struct VPaintData : public VPaintDataBase { struct VertProjHandle *vp_handle; CoNo *vertexcosnos; - /** - * Modify #Mesh.mloopcol directly, since the derived mesh is drawing from this - * array, otherwise we need to refresh the modifier stack. - */ - bool use_fast_update; - - /* loops tagged as having been painted, to apply shared vertex color - * blending only to modified loops */ - bool *mlooptag; - bool is_texbrush; /* Special storage for smear brush, avoid feedback loop - update each step. */ @@ -2893,20 +2856,6 @@ static void *vpaint_init_vpaint(bContext *C, vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex; - /* are we painting onto a modified mesh?, - * if not we can skip face map trickiness */ - if (vertex_paint_use_fast_update_check(ob)) { - vpd->use_fast_update = true; - } - else { - vpd->use_fast_update = false; - } - - /* to keep tracked of modified loops for shared vertex color blending */ - if (brush->vertexpaint_tool == VPAINT_TOOL_BLUR) { - vpd->mlooptag = (bool *)MEM_mallocN(sizeof(bool) * elem_num, "VPaintData mlooptag"); - } - if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); @@ -3035,10 +2984,10 @@ static void do_vpaint_brush_blur_loops(bContext *C, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + 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 = &me->mvert[v_index]; + const MVert *mv = &ss->mvert[v_index]; /* If the vertex is selected for painting. */ if (!use_vert_sel || mv->flag & SELECT) { @@ -3061,7 +3010,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 = &me->mpoly[p_index]; + const MPoly *mp = &ss->mpoly[p_index]; if (!use_face_sel || mp->flag & ME_FACE_SEL) { total_hit_loops += mp->totloop; for (int k = 0; k < mp->totloop; k++) { @@ -3095,8 +3044,8 @@ static void do_vpaint_brush_blur_loops(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { 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(me->mloop[l_index].v == v_index); - const MPoly *mp = &me->mpoly[p_index]; + 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) { Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ @@ -3177,10 +3126,10 @@ static void do_vpaint_brush_blur_verts(bContext *C, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + 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 = &me->mvert[v_index]; + const MVert *mv = &ss->mvert[v_index]; /* If the vertex is selected for painting. */ if (!use_vert_sel || mv->flag & SELECT) { @@ -3203,12 +3152,12 @@ 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 = &me->mpoly[p_index]; + const MPoly *mp = &ss->mpoly[p_index]; if (!use_face_sel || mp->flag & ME_FACE_SEL) { total_hit_loops += mp->totloop; for (int k = 0; k < mp->totloop; k++) { const uint l_index = mp->loopstart + k; - const uint v_index = me->mloop[l_index].v; + const uint v_index = ss->mloop[l_index].v; Color *col = lcol + v_index; @@ -3239,9 +3188,9 @@ static void do_vpaint_brush_blur_verts(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { const int p_index = gmap->vert_to_poly[v_index].indices[j]; - BLI_assert(me->mloop[gmap->vert_to_loop[v_index].indices[j]].v == v_index); + BLI_assert(ss->mloop[gmap->vert_to_loop[v_index].indices[j]].v == v_index); - const MPoly *mp = &me->mpoly[p_index]; + const MPoly *mp = &ss->mpoly[p_index]; if (!use_face_sel || mp->flag & ME_FACE_SEL) { Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ @@ -3328,10 +3277,10 @@ static void do_vpaint_brush_smear(bContext *C, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + 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_curr = &me->mvert[v_index]; + const MVert *mv_curr = &ss->mvert[v_index]; /* if the vertex is selected for painting. */ if (!use_vert_sel || mv_curr->flag & SELECT) { @@ -3360,15 +3309,15 @@ static void do_vpaint_brush_smear(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { 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(me->mloop[l_index].v == v_index); + BLI_assert(ss->mloop[l_index].v == v_index); UNUSED_VARS_NDEBUG(l_index); - const MPoly *mp = &me->mpoly[p_index]; + const MPoly *mp = &ss->mpoly[p_index]; if (!use_face_sel || mp->flag & ME_FACE_SEL) { - const MLoop *ml_other = &me->mloop[mp->loopstart]; + 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; if (v_other_index != v_index) { - const MVert *mv_other = &me->mvert[v_other_index]; + const MVert *mv_other = &ss->mvert[v_other_index]; /* Get the direction from the * selected vert to the neighbor. */ @@ -3414,10 +3363,10 @@ static void do_vpaint_brush_smear(bContext *C, else { const int l_index = gmap->vert_to_loop[v_index].indices[j]; elem_index = l_index; - BLI_assert(me->mloop[l_index].v == v_index); + BLI_assert(ss->mloop[l_index].v == v_index); } - const MPoly *mp = &me->mpoly[p_index]; + const MPoly *mp = &ss->mpoly[p_index]; if (!use_face_sel || mp->flag & ME_FACE_SEL) { /* Get the previous element color */ Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ @@ -3490,11 +3439,11 @@ static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd, BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : 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 = &me->mvert[v_index]; + const MVert *mv = &ss->mvert[v_index]; if (!use_vert_sel || mv->flag & SELECT) { accum2->len += gmap->vert_to_loop[v_index].count; /* if a vertex is within the brush region, then add its color to the blend. */ @@ -3610,10 +3559,10 @@ static void vpaint_do_draw(bContext *C, /* NOTE: Grids are 1:1 with corners (aka loops). * For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + 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 = &me->mvert[v_index]; + const MVert *mv = &ss->mvert[v_index]; /* If the vertex is selected for painting. */ if (!use_vert_sel || mv->flag & SELECT) { @@ -3667,8 +3616,8 @@ static void vpaint_do_draw(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { 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(me->mloop[l_index].v == v_index); - const MPoly *mp = &me->mpoly[p_index]; + 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) { Color color_orig = Color(0, 0, 0, 0); /* unused when array is nullptr */ @@ -3901,15 +3850,7 @@ static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, P ED_region_tag_redraw(vc->region); - if (vpd->use_fast_update == false) { - /* recalculate modifier stack to get new colors, slow, - * avoid this if we can! */ - DEG_id_tag_update((ID *)ob->data, 0); - } - else { - /* Flush changes through DEG. */ - DEG_id_tag_update((ID *)ob->data, ID_RECALC_COPY_ON_WRITE); - } + DEG_id_tag_update((ID *)ob->data, ID_RECALC_GEOMETRY); } static void vpaint_stroke_update_step(bContext *C, @@ -3950,9 +3891,6 @@ static void vpaint_free_vpaintdata(Object *UNUSED(ob), void *_vpd) ED_vpaint_proj_handle_free(vpd->vp_handle); } - if (vpd->mlooptag) { - MEM_freeN(vpd->mlooptag); - } if (vpd->smear.color_prev) { MEM_freeN(vpd->smear.color_prev); } @@ -4023,7 +3961,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) BKE_pbvh_ensure_node_loops(ob->sculpt->pbvh); } - SCULPT_undo_push_begin(ob, "Vertex Paint"); + SCULPT_undo_push_begin_ex(ob, "Vertex Paint"); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { paint_stroke_free(C, op, (PaintStroke *)op->customdata); @@ -4099,7 +4037,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot) * \{ */ template<typename Color, typename Traits, eAttrDomain domain> -static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, Color *color_layer) +static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, CustomDataLayer *layer) { Mesh *me; if (((me = BKE_mesh_from_object(ob)) == nullptr) || @@ -4112,30 +4050,73 @@ static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, Color *color_ const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - const MPoly *mp = me->mpoly; - for (int i = 0; i < me->totpoly; i++, mp++) { - if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { - continue; + if (me->edit_mesh) { + BMesh *bm = me->edit_mesh->bm; + BMFace *f; + BMIter iter; + + int cd_offset = -1; + + /* Find customdata offset inside of bmesh. */ + CustomData *cdata = domain == ATTR_DOMAIN_POINT ? &bm->vdata : &bm->ldata; + + for (int i = 0; i < cdata->totlayer; i++) { + if (STREQ(cdata->layers[i].name, layer->name)) { + cd_offset = layer->offset; + } } - int j = 0; - do { - uint vidx = me->mloop[mp->loopstart + j].v; + BLI_assert(cd_offset != -1); - if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) { - if constexpr (domain == ATTR_DOMAIN_CORNER) { - color_layer[mp->loopstart + j] = paintcol; - } - else { - color_layer[vidx] = paintcol; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + Color *color; + BMLoop *l = f->l_first; + + do { + 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)); + } + else { + color = static_cast<Color *>(BM_ELEM_CD_GET_VOID_P(l->v, cd_offset)); + } + + *color = paintcol; } - } - j++; - } while (j < mp->totloop); + } while ((l = l->next) != f->l_first); + } } + 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)) { + continue; + } - /* remove stale me->mcol, will be added later */ - BKE_mesh_tessface_clear(me); + int j = 0; + do { + uint vidx = loops[poly.loopstart + j].v; + + if (!(use_vert_sel && !(verts[vidx].flag & SELECT))) { + if constexpr (domain == ATTR_DOMAIN_CORNER) { + color_layer[poly.loopstart + j] = paintcol; + } + else { + color_layer[vidx] = paintcol; + } + } + j++; + } while (j < poly.totloop); + } + + /* remove stale me->mcol, will be added later */ + BKE_mesh_tessface_clear(me); + } DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE); @@ -4160,7 +4141,7 @@ static bool paint_object_attributes_active_color_fill_ex(Object *ob, if (!layer) { return false; } - /* Store original #Mesh.editflag.*/ + /* Store original #Mesh.editflag. */ const decltype(me->editflag) editflag = me->editflag; if (!only_selected) { me->editflag &= ~ME_EDIT_PAINT_FACE_SEL; @@ -4170,22 +4151,18 @@ static bool paint_object_attributes_active_color_fill_ex(Object *ob, bool ok = false; if (domain == ATTR_DOMAIN_POINT) { if (layer->type == CD_PROP_COLOR) { - ok = vertex_color_set<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>( - ob, fill_color, static_cast<ColorPaint4f *>(layer->data)); + ok = vertex_color_set<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(ob, fill_color, layer); } else if (layer->type == CD_PROP_BYTE_COLOR) { - ok = vertex_color_set<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>( - ob, fill_color, static_cast<ColorPaint4b *>(layer->data)); + ok = vertex_color_set<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>(ob, fill_color, layer); } } else { if (layer->type == CD_PROP_COLOR) { - ok = vertex_color_set<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>( - ob, fill_color, static_cast<ColorPaint4f *>(layer->data)); + ok = vertex_color_set<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>(ob, fill_color, layer); } else if (layer->type == CD_PROP_BYTE_COLOR) { - ok = vertex_color_set<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>( - ob, fill_color, static_cast<ColorPaint4b *>(layer->data)); + ok = vertex_color_set<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(ob, fill_color, layer); } } /* Restore #Mesh.editflag. */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c deleted file mode 100644 index a2e1adff50a..00000000000 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ /dev/null @@ -1,485 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup edsculpt - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BLI_math_base.h" -#include "BLI_math_color.h" - -#include "BKE_context.h" -#include "BKE_deform.h" -#include "BKE_mesh.h" - -#include "DEG_depsgraph.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "ED_mesh.h" - -#include "paint_intern.h" /* own include */ - -/* -------------------------------------------------------------------- */ -/** \name Internal Utility Functions - * \{ */ - -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))) && - (me && me->totpoly && me->dvert); -} - -static void tag_object_after_update(Object *object) -{ - BLI_assert(object->type == OB_MESH); - Mesh *mesh = object->data; - DEG_id_tag_update(&mesh->id, ID_RECALC_COPY_ON_WRITE); - /* NOTE: Original mesh is used for display, so tag it directly here. */ - BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Vertex Color from Weight Operator - * \{ */ - -static bool vertex_paint_from_weight(Object *ob) -{ - Mesh *me; - const MPoly *mp; - int vgroup_active; - - if (((me = BKE_mesh_from_object(ob)) == NULL || (ED_mesh_color_ensure(me, NULL)) == false)) { - return false; - } - - /* TODO: respect selection. */ - /* TODO: Do we want to take weights from evaluated mesh instead? 2.7x was not doing it anyway. */ - mp = me->mpoly; - vgroup_active = me->vertex_group_active_index - 1; - for (int i = 0; i < me->totpoly; i++, mp++) { - MLoopCol *lcol = &me->mloopcol[mp->loopstart]; - uint j = 0; - do { - uint vidx = me->mloop[mp->loopstart + j].v; - const float weight = BKE_defvert_find_weight(&me->dvert[vidx], vgroup_active); - const uchar grayscale = weight * 255; - lcol->r = grayscale; - lcol->b = grayscale; - lcol->g = grayscale; - lcol++; - j++; - } while (j < mp->totloop); - } - - tag_object_after_update(ob); - - return true; -} - -static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *obact = CTX_data_active_object(C); - if (vertex_paint_from_weight(obact)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Vertex Color from Weight"; - ot->idname = "PAINT_OT_vertex_color_from_weight"; - ot->description = "Convert active weight into gray scale vertex colors"; - - /* api callback */ - ot->exec = vertex_paint_from_weight_exec; - ot->poll = vertex_weight_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* TODO: invert, alpha */ -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Smooth Vertex Colors Operator - * \{ */ - -static void vertex_color_smooth_looptag(Mesh *me, const bool *mlooptag) -{ - const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const MPoly *mp; - int(*scol)[4]; - bool has_shared = false; - - if (me->mloopcol == NULL || me->totvert == 0 || me->totpoly == 0) { - return; - } - - scol = MEM_callocN(sizeof(int) * me->totvert * 5, "scol"); - - int i; - for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) { - if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) { - const MLoop *ml = me->mloop + mp->loopstart; - MLoopCol *lcol = me->mloopcol + mp->loopstart; - for (int j = 0; j < mp->totloop; j++, ml++, lcol++) { - scol[ml->v][0] += lcol->r; - scol[ml->v][1] += lcol->g; - scol[ml->v][2] += lcol->b; - scol[ml->v][3] += 1; - has_shared = 1; - } - } - } - - if (has_shared) { - for (i = 0; i < me->totvert; i++) { - if (scol[i][3] != 0) { - scol[i][0] = divide_round_i(scol[i][0], scol[i][3]); - scol[i][1] = divide_round_i(scol[i][1], scol[i][3]); - scol[i][2] = divide_round_i(scol[i][2], scol[i][3]); - } - } - - for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) { - if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) { - const MLoop *ml = me->mloop + mp->loopstart; - MLoopCol *lcol = me->mloopcol + mp->loopstart; - for (int j = 0; j < mp->totloop; j++, ml++, lcol++) { - if (mlooptag[mp->loopstart + j]) { - lcol->r = scol[ml->v][0]; - lcol->g = scol[ml->v][1]; - lcol->b = scol[ml->v][2]; - } - } - } - } - } - - MEM_freeN(scol); -} - -static bool vertex_color_smooth(Object *ob) -{ - Mesh *me; - const MPoly *mp; - int i, j; - - bool *mlooptag; - - if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { - return false; - } - - const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - - mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); - - /* simply tag loops of selected faces */ - mp = me->mpoly; - for (i = 0; i < me->totpoly; i++, mp++) { - const MLoop *ml = me->mloop + mp->loopstart; - - if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { - continue; - } - - j = 0; - do { - if (!(use_vert_sel && !(me->mvert[ml->v].flag & SELECT))) { - mlooptag[mp->loopstart + j] = true; - } - ml++; - j++; - } while (j < mp->totloop); - } - - /* remove stale me->mcol, will be added later */ - BKE_mesh_tessface_clear(me); - - vertex_color_smooth_looptag(me, mlooptag); - - MEM_freeN(mlooptag); - - tag_object_after_update(ob); - - return true; -} - -static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *obact = CTX_data_active_object(C); - if (vertex_color_smooth(obact)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void PAINT_OT_vertex_color_smooth(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Smooth Vertex Colors"; - ot->idname = "PAINT_OT_vertex_color_smooth"; - ot->description = "Smooth colors across vertices"; - - /* api callbacks */ - ot->exec = vertex_color_smooth_exec; - ot->poll = vertex_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Vertex Color Transformation Operators - * \{ */ - -struct VPaintTx_BrightContrastData { - /* pre-calculated */ - float gain; - float offset; -}; - -static void vpaint_tx_brightness_contrast(const float col[3], - const void *user_data, - float r_col[3]) -{ - const struct VPaintTx_BrightContrastData *data = user_data; - - for (int i = 0; i < 3; i++) { - r_col[i] = data->gain * col[i] + data->offset; - } -} - -static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op) -{ - Object *obact = CTX_data_active_object(C); - - float gain, offset; - { - float brightness = RNA_float_get(op->ptr, "brightness"); - float contrast = RNA_float_get(op->ptr, "contrast"); - brightness /= 100.0f; - float delta = contrast / 200.0f; - /* - * The algorithm is by Werner D. Streidt - * (http://visca.com/ffactory/archives/5-99/msg00021.html) - * Extracted of OpenCV demhist.c - */ - if (contrast > 0) { - gain = 1.0f - delta * 2.0f; - gain = 1.0f / max_ff(gain, FLT_EPSILON); - offset = gain * (brightness - delta); - } - else { - delta *= -1; - gain = max_ff(1.0f - delta * 2.0f, 0.0f); - offset = gain * brightness + delta; - } - } - - const struct VPaintTx_BrightContrastData user_data = { - .gain = gain, - .offset = offset, - }; - - if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Vertex Paint Brightness/Contrast"; - ot->idname = "PAINT_OT_vertex_color_brightness_contrast"; - ot->description = "Adjust vertex color brightness/contrast"; - - /* api callbacks */ - ot->exec = vertex_color_brightness_contrast_exec; - ot->poll = vertex_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* params */ - const float min = -100, max = +100; - prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max); - prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max); - RNA_def_property_ui_range(prop, min, max, 1, 1); -} - -struct VPaintTx_HueSatData { - float hue; - float sat; - float val; -}; - -static void vpaint_tx_hsv(const float col[3], const void *user_data, float r_col[3]) -{ - const struct VPaintTx_HueSatData *data = user_data; - float hsv[3]; - rgb_to_hsv_v(col, hsv); - - hsv[0] += (data->hue - 0.5f); - if (hsv[0] > 1.0f) { - hsv[0] -= 1.0f; - } - else if (hsv[0] < 0.0f) { - hsv[0] += 1.0f; - } - hsv[1] *= data->sat; - hsv[2] *= data->val; - - hsv_to_rgb_v(hsv, r_col); -} - -static int vertex_color_hsv_exec(bContext *C, wmOperator *op) -{ - Object *obact = CTX_data_active_object(C); - - const struct VPaintTx_HueSatData user_data = { - .hue = RNA_float_get(op->ptr, "h"), - .sat = RNA_float_get(op->ptr, "s"), - .val = RNA_float_get(op->ptr, "v"), - }; - - if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void PAINT_OT_vertex_color_hsv(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Vertex Paint Hue Saturation Value"; - ot->idname = "PAINT_OT_vertex_color_hsv"; - ot->description = "Adjust vertex color HSV values"; - - /* api callbacks */ - ot->exec = vertex_color_hsv_exec; - ot->poll = vertex_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* params */ - RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f); - RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f); - RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f); -} - -static void vpaint_tx_invert(const float col[3], const void *UNUSED(user_data), float r_col[3]) -{ - for (int i = 0; i < 3; i++) { - r_col[i] = 1.0f - col[i]; - } -} - -static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *obact = CTX_data_active_object(C); - - if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void PAINT_OT_vertex_color_invert(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Vertex Paint Invert"; - ot->idname = "PAINT_OT_vertex_color_invert"; - ot->description = "Invert RGB values"; - - /* api callbacks */ - ot->exec = vertex_color_invert_exec; - ot->poll = vertex_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -struct VPaintTx_LevelsData { - float gain; - float offset; -}; - -static void vpaint_tx_levels(const float col[3], const void *user_data, float r_col[3]) -{ - const struct VPaintTx_LevelsData *data = user_data; - for (int i = 0; i < 3; i++) { - r_col[i] = data->gain * (col[i] + data->offset); - } -} - -static int vertex_color_levels_exec(bContext *C, wmOperator *op) -{ - Object *obact = CTX_data_active_object(C); - - const struct VPaintTx_LevelsData user_data = { - .gain = RNA_float_get(op->ptr, "gain"), - .offset = RNA_float_get(op->ptr, "offset"), - }; - - if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void PAINT_OT_vertex_color_levels(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Vertex Paint Levels"; - ot->idname = "PAINT_OT_vertex_color_levels"; - ot->description = "Adjust levels of vertex colors"; - - /* api callbacks */ - ot->exec = vertex_color_levels_exec; - ot->poll = vertex_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* params */ - RNA_def_float( - ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f); - RNA_def_float( - ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f); -} - -/** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc new file mode 100644 index 00000000000..10ad4c2192f --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc @@ -0,0 +1,514 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_array.hh" +#include "BLI_index_mask_ops.hh" +#include "BLI_math_base.h" +#include "BLI_math_color.h" +#include "BLI_vector.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_geometry_set.hh" +#include "BKE_mesh.h" + +#include "DEG_depsgraph.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_mesh.h" + +#include "paint_intern.h" /* own include */ + +using blender::Array; +using blender::ColorGeometry4f; +using blender::GMutableSpan; +using blender::IndexMask; +using blender::Vector; + +/* -------------------------------------------------------------------- */ +/** \name Internal Utility Functions + * \{ */ + +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))) && + (me && me->totpoly && !me->deform_verts().is_empty()); +} + +static void tag_object_after_update(Object *object) +{ + BLI_assert(object->type == OB_MESH); + Mesh *mesh = static_cast<Mesh *>(object->data); + DEG_id_tag_update(&mesh->id, ID_RECALC_COPY_ON_WRITE); + /* NOTE: Original mesh is used for display, so tag it directly here. */ + BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex Color from Weight Operator + * \{ */ + +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)) { + return false; + } + + const CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&me->id); + if (active_color_layer == nullptr) { + BLI_assert_unreachable(); + return false; + } + + const int active_vertex_group_index = me->vertex_group_active_index - 1; + const bDeformGroup *deform_group = static_cast<const bDeformGroup *>( + BLI_findlink(&me->vertex_group_names, active_vertex_group_index)); + if (deform_group == nullptr) { + BLI_assert_unreachable(); + return false; + } + + bke::MutableAttributeAccessor attributes = me->attributes_for_write(); + + bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name); + if (!color_attribute) { + BLI_assert_unreachable(); + return false; + } + + /* Retrieve the vertex group with the domain and type of the existing color + * attribute, in order to let the attribute API handle both conversions. */ + const GVArray vertex_group = attributes.lookup( + deform_group->name, + ATTR_DOMAIN_POINT, + bke::cpp_type_to_custom_data_type(color_attribute.varray.type())); + if (!vertex_group) { + BLI_assert_unreachable(); + return false; + } + + GVArraySpan interpolated{ + attributes.adapt_domain(vertex_group, ATTR_DOMAIN_POINT, color_attribute.domain)}; + + color_attribute.varray.set_all(interpolated.data()); + color_attribute.finish(); + tag_object_after_update(ob); + + return true; +} + +static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *obact = CTX_data_active_object(C); + if (vertex_paint_from_weight(obact)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Color from Weight"; + ot->idname = "PAINT_OT_vertex_color_from_weight"; + ot->description = "Convert active weight into gray scale vertex colors"; + + /* api callback */ + ot->exec = vertex_paint_from_weight_exec; + ot->poll = vertex_weight_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* TODO: invert, alpha */ +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Smooth Vertex Colors Operator + * \{ */ + +static IndexMask get_selected_indices(const Mesh &mesh, + const eAttrDomain domain, + Vector<int64_t> &indices) +{ + using namespace blender; + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + + 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); + + return index_mask_ops::find_indices_from_virtual_array( + IndexMask(attributes.domain_size(domain)), 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); + + return index_mask_ops::find_indices_from_virtual_array( + IndexMask(attributes.domain_size(domain)), selection, 4096, indices); + } + return IndexMask(attributes.domain_size(domain)); +} + +static void face_corner_color_equalize_verts(Mesh &mesh, const IndexMask selection) +{ + using namespace blender; + + const CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&mesh.id); + if (active_color_layer == nullptr) { + BLI_assert_unreachable(); + return; + } + + bke::AttributeAccessor attributes = mesh.attributes(); + + if (attributes.lookup_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) { + return; + } + + GVArray color_attribute_point = attributes.lookup(active_color_layer->name, ATTR_DOMAIN_POINT); + + GVArray color_attribute_corner = attributes.adapt_domain( + color_attribute_point, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER); + + color_attribute_corner.materialize(selection, active_color_layer->data); +} + +static bool vertex_color_smooth(Object *ob) +{ + Mesh *me; + if (((me = BKE_mesh_from_object(ob)) == nullptr) || + (ED_mesh_color_ensure(me, nullptr) == false)) { + return false; + } + + Vector<int64_t> indices; + const IndexMask selection = get_selected_indices(*me, ATTR_DOMAIN_CORNER, indices); + + face_corner_color_equalize_verts(*me, selection); + + tag_object_after_update(ob); + + return true; +} + +static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *obact = CTX_data_active_object(C); + if (vertex_color_smooth(obact)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +void PAINT_OT_vertex_color_smooth(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Smooth Vertex Colors"; + ot->idname = "PAINT_OT_vertex_color_smooth"; + ot->description = "Smooth colors across vertices"; + + /* api callbacks */ + ot->exec = vertex_color_smooth_exec; + ot->poll = vertex_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex Color Transformation Operators + * \{ */ + +template<typename TransformFn> +static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn) +{ + using namespace blender; + + const CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&mesh.id); + if (active_color_layer == nullptr) { + BLI_assert_unreachable(); + return false; + } + + bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); + + bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name); + if (!color_attribute) { + BLI_assert_unreachable(); + return false; + } + + Vector<int64_t> indices; + const IndexMask selection = get_selected_indices(mesh, color_attribute.domain, indices); + + attribute_math::convert_to_static_type(color_attribute.varray.type(), [&](auto dummy) { + using T = decltype(dummy); + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for ([[maybe_unused]] const int i : selection.slice(range)) { + if constexpr (std::is_same_v<T, ColorGeometry4f>) { + ColorGeometry4f color = color_attribute.varray.get<ColorGeometry4f>(i); + transform_fn(color); + color_attribute.varray.set_by_copy(i, &color); + } + else if constexpr (std::is_same_v<T, ColorGeometry4b>) { + ColorGeometry4f color = color_attribute.varray.get<ColorGeometry4b>(i).decode(); + transform_fn(color); + ColorGeometry4b color_encoded = color.encode(); + color_attribute.varray.set_by_copy(i, &color_encoded); + } + } + }); + }); + + color_attribute.finish(); + + DEG_id_tag_update(&mesh.id, 0); + + return true; +} + +static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op) +{ + Object *obact = CTX_data_active_object(C); + + float gain, offset; + { + float brightness = RNA_float_get(op->ptr, "brightness"); + float contrast = RNA_float_get(op->ptr, "contrast"); + brightness /= 100.0f; + float delta = contrast / 200.0f; + /* + * The algorithm is by Werner D. Streidt + * (http://visca.com/ffactory/archives/5-99/msg00021.html) + * Extracted of OpenCV demhist.c + */ + if (contrast > 0) { + gain = 1.0f - delta * 2.0f; + gain = 1.0f / max_ff(gain, FLT_EPSILON); + offset = gain * (brightness - delta); + } + else { + delta *= -1; + gain = max_ff(1.0f - delta * 2.0f, 0.0f); + offset = gain * brightness + delta; + } + } + + Mesh *me; + if (((me = BKE_mesh_from_object(obact)) == nullptr) || + (ED_mesh_color_ensure(me, nullptr) == false)) { + return OPERATOR_CANCELLED; + } + + transform_active_color(*me, [&](ColorGeometry4f &color) { + for (int i = 0; i < 3; i++) { + color[i] = gain * color[i] + offset; + } + }); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + + return OPERATOR_FINISHED; +} + +void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Vertex Paint Brightness/Contrast"; + ot->idname = "PAINT_OT_vertex_color_brightness_contrast"; + ot->description = "Adjust vertex color brightness/contrast"; + + /* api callbacks */ + ot->exec = vertex_color_brightness_contrast_exec; + ot->poll = vertex_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + const float min = -100, max = +100; + prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max); + prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max); + RNA_def_property_ui_range(prop, min, max, 1, 1); +} + +static int vertex_color_hsv_exec(bContext *C, wmOperator *op) +{ + Object *obact = CTX_data_active_object(C); + + const float hue = RNA_float_get(op->ptr, "h"); + const float sat = RNA_float_get(op->ptr, "s"); + const float val = RNA_float_get(op->ptr, "v"); + + Mesh *me; + if (((me = BKE_mesh_from_object(obact)) == nullptr) || + (ED_mesh_color_ensure(me, nullptr) == false)) { + return OPERATOR_CANCELLED; + } + + transform_active_color(*me, [&](ColorGeometry4f &color) { + float hsv[3]; + rgb_to_hsv_v(color, hsv); + + hsv[0] += (hue - 0.5f); + if (hsv[0] > 1.0f) { + hsv[0] -= 1.0f; + } + else if (hsv[0] < 0.0f) { + hsv[0] += 1.0f; + } + hsv[1] *= sat; + hsv[2] *= val; + + hsv_to_rgb_v(hsv, color); + }); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + + return OPERATOR_FINISHED; +} + +void PAINT_OT_vertex_color_hsv(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Paint Hue Saturation Value"; + ot->idname = "PAINT_OT_vertex_color_hsv"; + ot->description = "Adjust vertex color HSV values"; + + /* api callbacks */ + ot->exec = vertex_color_hsv_exec; + ot->poll = vertex_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f); + RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f); + 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)) +{ + Object *obact = CTX_data_active_object(C); + + Mesh *me; + if (((me = BKE_mesh_from_object(obact)) == nullptr) || + (ED_mesh_color_ensure(me, nullptr) == false)) { + return OPERATOR_CANCELLED; + } + + transform_active_color(*me, [&](ColorGeometry4f &color) { + for (int i = 0; i < 3; i++) { + color[i] = 1.0f - color[i]; + } + }); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + + return OPERATOR_FINISHED; +} + +void PAINT_OT_vertex_color_invert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Paint Invert"; + ot->idname = "PAINT_OT_vertex_color_invert"; + ot->description = "Invert RGB values"; + + /* api callbacks */ + ot->exec = vertex_color_invert_exec; + ot->poll = vertex_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int vertex_color_levels_exec(bContext *C, wmOperator *op) +{ + Object *obact = CTX_data_active_object(C); + + const float gain = RNA_float_get(op->ptr, "gain"); + const float offset = RNA_float_get(op->ptr, "offset"); + + Mesh *me; + if (((me = BKE_mesh_from_object(obact)) == nullptr) || + (ED_mesh_color_ensure(me, nullptr) == false)) { + return OPERATOR_CANCELLED; + } + + transform_active_color(*me, [&](ColorGeometry4f &color) { + for (int i = 0; i < 3; i++) { + color[i] = gain * (color[i] + offset); + } + }); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + + return OPERATOR_FINISHED; +} + +void PAINT_OT_vertex_color_levels(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Paint Levels"; + ot->idname = "PAINT_OT_vertex_color_levels"; + ot->description = "Adjust levels of vertex colors"; + + /* api callbacks */ + ot->exec = vertex_color_levels_exec; + ot->poll = vertex_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + RNA_def_float( + ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f); + RNA_def_float( + ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f); +} + +/** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c deleted file mode 100644 index cd099f71ccd..00000000000 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup edsculpt - * - * Intended for use by `paint_vertex.c` & `paint_vertex_color_ops.c`. - */ - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" - -#include "BLI_math_base.h" -#include "BLI_math_color.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf.h" - -#include "BKE_context.h" -#include "BKE_mesh.h" - -#include "DEG_depsgraph.h" - -#include "ED_mesh.h" - -#include "paint_intern.h" /* own include */ - -#define EPS_SATURATION 0.0005f - -bool ED_vpaint_color_transform(struct Object *ob, - VPaintTransform_Callback vpaint_tx_fn, - const void *user_data) -{ - Mesh *me; - const MPoly *mp; - int i, j; - - if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { - return false; - } - - const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - - mp = me->mpoly; - for (i = 0; i < me->totpoly; i++, mp++) { - MLoopCol *lcol = me->mloopcol + mp->loopstart; - - if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { - continue; - } - - j = 0; - do { - uint vidx = me->mloop[mp->loopstart + j].v; - if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) { - float col_mix[3]; - rgb_uchar_to_float(col_mix, &lcol->r); - - vpaint_tx_fn(col_mix, user_data, col_mix); - - rgb_float_to_uchar(&lcol->r, col_mix); - } - lcol++; - j++; - } while (j < mp->totloop); - } - - /* remove stale me->mcol, will be added later */ - BKE_mesh_tessface_clear(me); - - DEG_id_tag_update(&me->id, 0); - - return true; -} 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 d98660d8939..0a0d7cff214 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -168,8 +168,9 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); + const MDeformVert *dvert = BKE_mesh_deform_verts(me); - if (me && me->dvert && vc.v3d && vc.rv3d && (me->vertex_group_active_index != 0)) { + if (me && dvert && vc.v3d && vc.rv3d && (me->vertex_group_active_index != 0)) { const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int v_idx_best = -1; uint index; @@ -200,7 +201,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even ToolSettings *ts = vc.scene->toolsettings; Brush *brush = BKE_paint_brush(&ts->wpaint->paint); const int vgroup_active = me->vertex_group_active_index - 1; - float vgroup_weight = BKE_defvert_find_weight(&me->dvert[v_idx_best], vgroup_active); + float vgroup_weight = BKE_defvert_find_weight(&dvert[v_idx_best], vgroup_active); const int defbase_tot = BLI_listbase_count(&me->vertex_group_names); bool use_lock_relative = ts->wpaint_lock_relative; bool *defbase_locked = NULL, *defbase_unlocked = NULL; @@ -232,7 +233,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even bool is_normalized = ts->auto_normalize || use_lock_relative; vgroup_weight = BKE_defvert_multipaint_collective_weight( - &me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, is_normalized); + &dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, is_normalized); } MEM_freeN(defbase_sel); @@ -243,7 +244,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even defbase_tot, defbase_locked, defbase_unlocked, defbase_locked, defbase_unlocked); vgroup_weight = BKE_defvert_lock_relative_weight( - vgroup_weight, &me->dvert[v_idx_best], defbase_tot, defbase_locked, defbase_unlocked); + vgroup_weight, &dvert[v_idx_best], defbase_tot, defbase_locked, defbase_unlocked); } MEM_SAFE_FREE(defbase_locked); @@ -316,8 +317,11 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); + const MPoly *polys = BKE_mesh_polys(me); + const MLoop *loops = BKE_mesh_loops(me); + const MDeformVert *dverts = BKE_mesh_deform_verts(me); - if (me && me->dvert && vc.v3d && vc.rv3d && me->vertex_group_names.first) { + if (me && dverts && vc.v3d && vc.rv3d && me->vertex_group_names.first) { const int defbase_tot = BLI_listbase_count(&me->vertex_group_names); const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups"); @@ -334,17 +338,17 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, if (use_vert_sel) { if (ED_mesh_pick_vert(C, vc.obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, true, &index)) { - MDeformVert *dvert = &me->dvert[index]; + const MDeformVert *dvert = &dverts[index]; found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups); } } else { if (ED_mesh_pick_face(C, vc.obact, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { - const MPoly *mp = &me->mpoly[index]; + const MPoly *mp = &polys[index]; uint fidx = mp->totloop - 1; do { - MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v]; + const MDeformVert *dvert = &dverts[loops[mp->loopstart + fidx].v]; found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups); } while (fidx--); } @@ -441,7 +445,12 @@ 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); - if (me->totpoly == 0 || me->dvert == NULL || !me->mpoly) { + 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); + + if (me->totpoly == 0 || dvert == NULL) { return false; } @@ -453,9 +462,9 @@ static bool weight_paint_set(Object *ob, float paintweight) } struct WPaintPrev wpp; - wpaint_prev_create(&wpp, me->dvert, me->totvert); + wpaint_prev_create(&wpp, dvert, me->totvert); - for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) { + 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)) { @@ -463,14 +472,14 @@ static bool weight_paint_set(Object *ob, float paintweight) } do { - uint vidx = me->mloop[mp->loopstart + fidx].v; + uint vidx = loops[mp->loopstart + fidx].v; - if (!me->dvert[vidx].flag) { - if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) { + if (!dvert[vidx].flag) { + if ((paint_selmode == SCE_SELECT_VERTEX) && !(verts[vidx].flag & SELECT)) { continue; } - dw = BKE_defvert_ensure_index(&me->dvert[vidx], vgroup_active); + dw = BKE_defvert_ensure_index(&dvert[vidx], vgroup_active); if (dw) { dw_prev = BKE_defvert_ensure_index(wpp.wpaint_prev + vidx, vgroup_active); dw_prev->weight = dw->weight; /* set the undo weight */ @@ -482,11 +491,11 @@ static bool weight_paint_set(Object *ob, float paintweight) if (j >= 0) { /* copy, not paint again */ if (vgroup_mirror != -1) { - dw = BKE_defvert_ensure_index(me->dvert + j, vgroup_mirror); + dw = BKE_defvert_ensure_index(dvert + j, vgroup_mirror); dw_prev = BKE_defvert_ensure_index(wpp.wpaint_prev + j, vgroup_mirror); } else { - dw = BKE_defvert_ensure_index(me->dvert + j, vgroup_active); + dw = BKE_defvert_ensure_index(dvert + j, vgroup_active); dw_prev = BKE_defvert_ensure_index(wpp.wpaint_prev + j, vgroup_active); } dw_prev->weight = dw->weight; /* set the undo weight */ @@ -494,14 +503,14 @@ static bool weight_paint_set(Object *ob, float paintweight) } } } - me->dvert[vidx].flag = 1; + dvert[vidx].flag = 1; } } while (fidx--); } { - MDeformVert *dv = me->dvert; + MDeformVert *dv = dvert; for (index = me->totvert; index != 0; index--, dv++) { dv->flag = 0; } @@ -574,6 +583,7 @@ typedef struct WPGradient_userData { struct ARegion *region; Scene *scene; Mesh *me; + MDeformVert *dvert; Brush *brush; const float *sco_start; /* [2] */ const float *sco_end; /* [2] */ @@ -593,7 +603,6 @@ typedef struct WPGradient_userData { static void gradientVert_update(WPGradient_userData *grad_data, int index) { - Mesh *me = grad_data->me; WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index]; /* Optionally restrict to assigned vertices only. */ @@ -617,7 +626,7 @@ static void gradientVert_update(WPGradient_userData *grad_data, int index) alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f); if (alpha != 0.0f) { - MDeformVert *dv = &me->dvert[index]; + MDeformVert *dv = &grad_data->dvert[index]; MDeformWeight *dw = BKE_defvert_ensure_index(dv, grad_data->def_nr); // dw->weight = alpha; // testing int tool = grad_data->brush->blend; @@ -631,7 +640,7 @@ static void gradientVert_update(WPGradient_userData *grad_data, int index) vs->flag |= VGRAD_STORE_IS_MODIFIED; } else { - MDeformVert *dv = &me->dvert[index]; + MDeformVert *dv = &grad_data->dvert[index]; if (vs->flag & VGRAD_STORE_DW_EXIST) { /* normally we NULL check, but in this case we know it exists */ MDeformWeight *dw = BKE_defvert_find_index(dv, grad_data->def_nr); @@ -669,10 +678,9 @@ static void gradientVertInit__mapFunc(void *userData, const float UNUSED(no[3])) { WPGradient_userData *grad_data = userData; - Mesh *me = grad_data->me; WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index]; - if (grad_data->use_select && !(me->mvert[index].flag & SELECT)) { + if (grad_data->use_select && !(grad_data->dvert[index].flag & SELECT)) { copy_v2_fl(vs->sco, FLT_MAX); return; } @@ -693,7 +701,7 @@ static void gradientVertInit__mapFunc(void *userData, return; } - MDeformVert *dv = &me->dvert[index]; + MDeformVert *dv = &grad_data->dvert[index]; const MDeformWeight *dw = BKE_defvert_find_index(dv, grad_data->def_nr); if (dw) { vs->weight_orig = dw->weight; @@ -727,8 +735,9 @@ static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEven if (vert_cache != NULL) { Mesh *me = ob->data; if (vert_cache->wpp.wpaint_prev) { - BKE_defvert_array_free_elems(me->dvert, me->totvert); - BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert); + MDeformVert *dvert = BKE_mesh_deform_verts_for_write(me); + BKE_defvert_array_free_elems(dvert, me->totvert); + BKE_defvert_array_copy(dvert, vert_cache->wpp.wpaint_prev, me->totvert); wpaint_prev_destroy(&vert_cache->wpp); } MEM_freeN(vert_cache); @@ -753,6 +762,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); Mesh *me = ob->data; + MDeformVert *dverts = BKE_mesh_deform_verts_for_write(me); int x_start = RNA_int_get(op->ptr, "xstart"); int y_start = RNA_int_get(op->ptr, "ystart"); int x_end = RNA_int_get(op->ptr, "xend"); @@ -774,7 +784,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) data.is_init = true; wpaint_prev_create( - &((WPGradient_vertStoreBase *)gesture->user_data.data)->wpp, me->dvert, me->totvert); + &((WPGradient_vertStoreBase *)gesture->user_data.data)->wpp, dverts, me->totvert); /* On initialization only, convert face -> vert sel. */ if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { @@ -797,6 +807,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) data.region = region; data.scene = scene; data.me = ob->data; + data.dvert = dverts; data.sco_start = sco_start; data.sco_end = sco_end; data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end); @@ -851,7 +862,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) const int vgroup_num = BLI_listbase_count(&me->vertex_group_names); bool *vgroup_validmap = BKE_object_defgroup_validmap_get(ob, vgroup_num); if (vgroup_validmap != NULL) { - MDeformVert *dvert = me->dvert; + MDeformVert *dvert = dverts; for (int i = 0; i < me->totvert; i++) { if ((data.vert_cache->elem[i].flag & VGRAD_STORE_IS_MODIFIED) != 0) { BKE_defvert_normalize_lock_single(&dvert[i], vgroup_validmap, vgroup_num, data.def_nr); 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 5a63af4149a..ac16631f115 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c @@ -59,7 +59,7 @@ bool ED_wpaint_ensure_data(bContext *C, } /* if nothing was added yet, we make dverts and a vertex deform group */ - if (!me->dvert) { + if (BKE_mesh_deform_verts(me) == NULL) { BKE_object_defgroup_data_create(&me->id); WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 9ce80e4a433..51ff064c58d 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -116,28 +116,28 @@ int SCULPT_vertex_count_get(SculptSession *ss) case PBVH_BMESH: return BM_mesh_elem_count(BKE_pbvh_get_bmesh(ss->pbvh), BM_VERT); case PBVH_GRIDS: - return BKE_pbvh_get_grid_num_vertices(ss->pbvh); + return BKE_pbvh_get_grid_num_verts(ss->pbvh); } return 0; } -const float *SCULPT_vertex_co_get(SculptSession *ss, int index) +const float *SCULPT_vertex_co_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { if (ss->shapekey_active || ss->deform_modifiers_active) { const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh); - return mverts[index].co; + return mverts[vertex.i].co; } - return ss->mvert[index].co; + return ss->mvert[vertex.i].co; } case PBVH_BMESH: - return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; + return ((BMVert *)vertex.i)->co; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index)); } @@ -158,31 +158,33 @@ bool SCULPT_has_colors(const SculptSession *ss) return ss->vcol || ss->mcol; } -void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4]) +void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4]) { - BKE_pbvh_vertex_color_get(ss->pbvh, index, r_color); + BKE_pbvh_vertex_color_get(ss->pbvh, vertex, r_color); } -void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4]) +void SCULPT_vertex_color_set(SculptSession *ss, PBVHVertRef vertex, const float color[4]) { - BKE_pbvh_vertex_color_set(ss->pbvh, index, color); + BKE_pbvh_vertex_color_set(ss->pbvh, vertex, color); } -void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) +void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { const float(*vert_normals)[3] = BKE_pbvh_get_vert_normals(ss->pbvh); - copy_v3_v3(no, vert_normals[index]); + copy_v3_v3(no, vert_normals[vertex.i]); break; } - case PBVH_BMESH: - copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no); + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + copy_v3_v3(no, v->no); break; + } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; copy_v3_v3(no, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index))); break; @@ -190,42 +192,42 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) } } -const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index) +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex) { if (ss->persistent_base) { - return ss->persistent_base[index].co; + return ss->persistent_base[BKE_pbvh_vertex_to_index(ss->pbvh, vertex)].co; } - return SCULPT_vertex_co_get(ss, index); + return SCULPT_vertex_co_get(ss, vertex); } -const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index) +const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, PBVHVertRef vertex) { - /* Always grab active shape key if the sculpt happens on shapekey. */ - if (ss->shapekey_active) { - const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh); - return mverts[index].co; - } + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + /* Always grab active shape key if the sculpt happens on shapekey. */ + if (ss->shapekey_active) { + const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh); + return mverts[vertex.i].co; + } - /* Sculpting on the base mesh. */ - if (ss->mvert) { - return ss->mvert[index].co; + /* Sculpting on the base mesh. */ + return ss->mvert[vertex.i].co; } /* Everything else, such as sculpting on multires. */ - return SCULPT_vertex_co_get(ss, index); + return SCULPT_vertex_co_get(ss, vertex); } -void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]) +void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, float r_co[3]) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_BMESH: - copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, vertex)); break; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, @@ -236,30 +238,30 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3] } } -void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]) +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]) { if (ss->persistent_base) { - copy_v3_v3(no, ss->persistent_base[index].no); + copy_v3_v3(no, ss->persistent_base[vertex.i].no); return; } - SCULPT_vertex_normal_get(ss, index, no); + SCULPT_vertex_normal_get(ss, vertex, no); } -float SCULPT_vertex_mask_get(SculptSession *ss, int index) +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[index]; + return ss->vmask[vertex.i]; case PBVH_BMESH: - v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); + v = (BMVert *)vertex.i; mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); return *mask; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; return *CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)); } @@ -268,12 +270,13 @@ float SCULPT_vertex_mask_get(SculptSession *ss, int index) return 0.0f; } -int SCULPT_active_vertex_get(SculptSession *ss) +PBVHVertRef SCULPT_active_vertex_get(SculptSession *ss) { if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH, PBVH_GRIDS)) { - return ss->active_vertex_index; + return ss->active_vertex; } - return 0; + + return BKE_pbvh_make_vref(PBVH_REF_NONE); } const float *SCULPT_active_vertex_co_get(SculptSession *ss) @@ -338,32 +341,37 @@ int SCULPT_active_face_set_get(SculptSession *ss) return SCULPT_FACE_SET_NONE; } -void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible) +void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible) { switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - SET_FLAG_FROM_TEST(ss->mvert[index].flag, !visible, ME_HIDE); - BKE_pbvh_vert_mark_update(ss->pbvh, index); + case PBVH_FACES: { + bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh); + hide_vert[vertex.i] = visible; break; - case PBVH_BMESH: - BM_elem_flag_set(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN, !visible); + } + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + BM_elem_flag_set(v, BM_ELEM_HIDDEN, !visible); break; + } case PBVH_GRIDS: break; } } -bool SCULPT_vertex_visible_get(SculptSession *ss, int index) +bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - return !(ss->mvert[index].flag & ME_HIDE); + case PBVH_FACES: { + const bool *hide_vert = BKE_pbvh_get_vert_hide(ss->pbvh); + return hide_vert == NULL || !hide_vert[vertex.i]; + } case PBVH_BMESH: - return !BM_elem_flag_test(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN); + return !BM_elem_flag_test((BMVert *)vertex.i, BM_ELEM_HIDDEN); case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; BLI_bitmap **grid_hidden = BKE_pbvh_get_grid_visibility(ss->pbvh); if (grid_hidden && grid_hidden[grid_index]) { return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index); @@ -436,12 +444,12 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible) } } -bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index) +bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int j = 0; j < ss->pmap[index].count; j++) { + 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) { return true; } @@ -456,12 +464,12 @@ bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index) return true; } -bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index) +bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int j = 0; j < ss->pmap[index].count; j++) { + 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) { return false; } @@ -472,7 +480,7 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index) return true; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + 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 ss->face_sets[face_index] > 0; } @@ -480,12 +488,12 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index) return true; } -void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set) +void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int j = 0; j < ss->pmap[index].count; j++) { + 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); } @@ -495,7 +503,7 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set) break; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + 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); @@ -505,13 +513,13 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set) } } -int SCULPT_vertex_face_set_get(SculptSession *ss, int index) +int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; + MeshElemMap *vert_map = &ss->pmap[vertex.i]; int face_set = 0; - for (int i = 0; i < ss->pmap[index].count; i++) { + for (int i = 0; i < ss->pmap[vertex.i].count; i++) { if (ss->face_sets[vert_map->indices[i]] > face_set) { face_set = abs(ss->face_sets[vert_map->indices[i]]); } @@ -522,7 +530,7 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, int index) return 0; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + 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 ss->face_sets[face_index]; } @@ -530,12 +538,12 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, int index) return 0; } -bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set) +bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int i = 0; i < ss->pmap[index].count; i++) { + MeshElemMap *vert_map = &ss->pmap[vertex.i]; + for (int i = 0; i < ss->pmap[vertex.i].count; i++) { if (ss->face_sets[vert_map->indices[i]] == face_set) { return true; } @@ -546,7 +554,7 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set) return true; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + 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 ss->face_sets[face_index] == face_set; } @@ -554,7 +562,7 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set) return true; } -void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob) +void SCULPT_visibility_sync_all_face_sets_to_verts(Object *ob) { SculptSession *ss = ob->sculpt; Mesh *mesh = BKE_object_get_original_mesh(ob); @@ -574,11 +582,11 @@ void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob) } static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSession *ss, - int index) + PBVHVertRef vertex) { - MeshElemMap *vert_map = &ss->pmap[index]; - const bool visible = SCULPT_vertex_visible_get(ss, index); - for (int i = 0; i < ss->pmap[index].count; i++) { + 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]]); } @@ -586,18 +594,17 @@ static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSe ss->face_sets[vert_map->indices[i]] = -abs(ss->face_sets[vert_map->indices[i]]); } } - BKE_pbvh_vert_mark_update(ss->pbvh, index); } void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss) { if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { for (int i = 0; i < ss->totfaces; i++) { - MPoly *poly = &ss->mpoly[i]; + const MPoly *poly = &ss->mpoly[i]; bool poly_visible = true; for (int l = 0; l < poly->totloop; l++) { - MLoop *loop = &ss->mloop[poly->loopstart + l]; - if (!SCULPT_vertex_visible_get(ss, (int)loop->v)) { + const MLoop *loop = &ss->mloop[poly->loopstart + l]; + if (!SCULPT_vertex_visible_get(ss, BKE_pbvh_make_vref(loop->v))) { poly_visible = false; } } @@ -637,9 +644,9 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss MeshElemMap *vert_map = &ss->pmap[v1]; int p1 = -1, p2 = -1; for (int i = 0; i < ss->pmap[v1].count; i++) { - MPoly *p = &ss->mpoly[vert_map->indices[i]]; + const MPoly *p = &ss->mpoly[vert_map->indices[i]]; for (int l = 0; l < p->totloop; l++) { - MLoop *loop = &ss->mloop[p->loopstart + l]; + const MLoop *loop = &ss->mloop[p->loopstart + l]; if (loop->v == v2) { if (p1 == -1) { p1 = vert_map->indices[i]; @@ -660,18 +667,18 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss return true; } -bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index) +bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - return sculpt_check_unique_face_set_in_base_mesh(ss, index); + return sculpt_check_unique_face_set_in_base_mesh(ss, vertex.i); } case PBVH_BMESH: return true; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; const SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, .y = vertex_index / key->grid_size}; @@ -715,10 +722,12 @@ int SCULPT_face_set_next_available_get(SculptSession *ss) #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 -static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neighbor_index) +static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, + PBVHVertRef neighbor, + int neighbor_index) { for (int i = 0; i < iter->size; i++) { - if (iter->neighbors[i] == neighbor_index) { + if (iter->neighbors[i].i == neighbor.i) { return; } } @@ -727,63 +736,74 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neigh iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; if (iter->neighbors == iter->neighbors_fixed) { - iter->neighbors = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); - memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(int) * iter->size); + iter->neighbors = MEM_mallocN(iter->capacity * sizeof(PBVHVertRef), "neighbor array"); + memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(PBVHVertRef) * iter->size); } else { iter->neighbors = MEM_reallocN_id( - iter->neighbors, iter->capacity * sizeof(int), "neighbor array"); + iter->neighbors, iter->capacity * sizeof(PBVHVertRef), "neighbor array"); + } + + if (iter->neighbor_indices == iter->neighbor_indices_fixed) { + iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); + memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size); + } + else { + iter->neighbor_indices = MEM_reallocN_id( + iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array"); } } - iter->neighbors[iter->size] = neighbor_index; + iter->neighbors[iter->size] = neighbor; + iter->neighbor_indices[iter->size] = neighbor_index; iter->size++; } -static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss, - int index, - SculptVertexNeighborIter *iter) +static void sculpt_vertex_neighbors_get_bmesh(PBVHVertRef vertex, SculptVertexNeighborIter *iter) { - BMVert *v = BM_vert_at_index(ss->bm, index); + BMVert *v = (BMVert *)vertex.i; BMIter liter; BMLoop *l; 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; BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { const BMVert *adj_v[2] = {l->prev->v, l->next->v}; for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { const BMVert *v_other = adj_v[i]; - if (BM_elem_index_get(v_other) != (int)index) { - sculpt_vertex_neighbor_add(iter, BM_elem_index_get(v_other)); + if (v_other != v) { + sculpt_vertex_neighbor_add( + iter, BKE_pbvh_make_vref((intptr_t)v_other), BM_elem_index_get(v_other)); } } } } static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, - int index, + PBVHVertRef vertex, SculptVertexNeighborIter *iter) { - MeshElemMap *vert_map = &ss->pmap[index]; + 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; - for (int i = 0; i < ss->pmap[index].count; i++) { + for (int i = 0; i < ss->pmap[vertex.i].count; i++) { if (ss->face_sets[vert_map->indices[i]] < 0) { /* Skip connectivity from hidden faces. */ continue; } const MPoly *p = &ss->mpoly[vert_map->indices[i]]; uint f_adj_v[2]; - if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) { + 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] != index) { - sculpt_vertex_neighbor_add(iter, f_adj_v[j]); + if (f_adj_v[j] != vertex.i) { + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(f_adj_v[j]), f_adj_v[j]); } } } @@ -791,14 +811,17 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, if (ss->fake_neighbors.use_fake_neighbors) { BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); - if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) { - sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]); + if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add( + iter, + BKE_pbvh_make_vref(ss->fake_neighbors.fake_neighbor_index[vertex.i]), + ss->fake_neighbors.fake_neighbor_index[vertex.i]); } } } static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, - const int index, + const PBVHVertRef vertex, const bool include_duplicates, SculptVertexNeighborIter *iter) { @@ -806,8 +829,8 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, * maybe provide coordinate and mask pointers directly rather than converting * back and forth between #CCGElem and global index. */ const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, @@ -820,17 +843,20 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, iter->num_duplicates = neighbors.num_duplicates; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; + iter->neighbor_indices = iter->neighbor_indices_fixed; for (int i = 0; i < neighbors.size; i++) { - sculpt_vertex_neighbor_add(iter, - neighbors.coords[i].grid_index * key->grid_area + - neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x); + int v = neighbors.coords[i].grid_index * key->grid_area + + neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x; + + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); } if (ss->fake_neighbors.use_fake_neighbors) { BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); - if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) { - sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]); + if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { + int v = ss->fake_neighbors.fake_neighbor_index[vertex.i]; + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); } } @@ -840,19 +866,19 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, } void SCULPT_vertex_neighbors_get(SculptSession *ss, - const int index, + const PBVHVertRef vertex, const bool include_duplicates, SculptVertexNeighborIter *iter) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - sculpt_vertex_neighbors_get_faces(ss, index, iter); + sculpt_vertex_neighbors_get_faces(ss, vertex, iter); return; case PBVH_BMESH: - sculpt_vertex_neighbors_get_bmesh(ss, index, iter); + sculpt_vertex_neighbors_get_bmesh(vertex, iter); return; case PBVH_GRIDS: - sculpt_vertex_neighbors_get_grids(ss, index, include_duplicates, iter); + sculpt_vertex_neighbors_get_grids(ss, vertex, include_duplicates, iter); return; } } @@ -863,24 +889,24 @@ static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, c return BLI_BITMAP_TEST(ss->vertex_info.boundary, index); } -bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index) +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, index)) { + if (!SCULPT_vertex_all_face_sets_visible_get(ss, vertex)) { return true; } - return sculpt_check_boundary_vertex_in_base_mesh(ss, index); + return sculpt_check_boundary_vertex_in_base_mesh(ss, vertex.i); } case PBVH_BMESH: { - BMVert *v = BM_vert_at_index(ss->bm, index); + BMVert *v = (BMVert *)vertex.i; return BM_vert_is_boundary(v); } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; const SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, .y = vertex_index / key->grid_size}; @@ -941,7 +967,7 @@ bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3], } typedef struct NearestVertexTLSData { - int nearest_vertex_index; + PBVHVertRef nearest_vertex; float nearest_vertex_distance_squared; } NearestVertexTLSData; @@ -958,7 +984,7 @@ static void do_nearest_vertex_get_task_cb(void *__restrict userdata, float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); if (distance_squared < nvtd->nearest_vertex_distance_squared && distance_squared < data->max_distance_squared) { - nvtd->nearest_vertex_index = vd.index; + nvtd->nearest_vertex = vd.vertex; nvtd->nearest_vertex_distance_squared = distance_squared; } } @@ -971,17 +997,17 @@ static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata), { NearestVertexTLSData *join = chunk_join; NearestVertexTLSData *nvtd = chunk; - if (join->nearest_vertex_index == -1) { - join->nearest_vertex_index = nvtd->nearest_vertex_index; + if (join->nearest_vertex.i == PBVH_REF_NONE) { + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { - join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } } -int SCULPT_nearest_vertex_get( +PBVHVertRef SCULPT_nearest_vertex_get( Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original) { SculptSession *ss = ob->sculpt; @@ -996,7 +1022,7 @@ int SCULPT_nearest_vertex_get( }; BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); if (totnode == 0) { - return -1; + return BKE_pbvh_make_vref(PBVH_REF_NONE); } SculptThreadedTaskData task_data = { @@ -1008,7 +1034,7 @@ int SCULPT_nearest_vertex_get( copy_v3_v3(task_data.nearest_vertex_search_co, co); NearestVertexTLSData nvtd; - nvtd.nearest_vertex_index = -1; + nvtd.nearest_vertex.i = PBVH_REF_NONE; nvtd.nearest_vertex_distance_squared = FLT_MAX; TaskParallelSettings settings; @@ -1020,7 +1046,7 @@ int SCULPT_nearest_vertex_get( MEM_SAFE_FREE(nodes); - return nvtd.nearest_vertex_index; + return nvtd.nearest_vertex; } bool SCULPT_is_symmetry_iteration_valid(char i, char symm) @@ -1075,23 +1101,27 @@ void SCULPT_floodfill_init(SculptSession *ss, SculptFloodFill *flood) int vertex_count = SCULPT_vertex_count_get(ss); SCULPT_vertex_random_access_ensure(ss); - flood->queue = BLI_gsqueue_new(sizeof(int)); - flood->visited_vertices = BLI_BITMAP_NEW(vertex_count, "visited vertices"); + flood->queue = BLI_gsqueue_new(sizeof(intptr_t)); + flood->visited_verts = BLI_BITMAP_NEW(vertex_count, "visited verts"); } -void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index) +void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex) { - BLI_gsqueue_push(flood->queue, &index); + BLI_gsqueue_push(flood->queue, &vertex); } -void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index) +void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, PBVHVertRef vertex) { - BLI_gsqueue_push(flood->queue, &index); - BLI_BITMAP_ENABLE(flood->visited_vertices, index); + BLI_gsqueue_push(flood->queue, &vertex); + BLI_BITMAP_ENABLE(flood->visited_verts, vertex.i); } -void SCULPT_floodfill_add_initial_with_symmetry( - Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, int index, float radius) +void SCULPT_floodfill_add_initial_with_symmetry(Sculpt *sd, + Object *ob, + SculptSession *ss, + SculptFloodFill *flood, + PBVHVertRef vertex, + float radius) { /* Add active vertex and symmetric vertices to the queue. */ const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -1099,18 +1129,19 @@ void SCULPT_floodfill_add_initial_with_symmetry( if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { continue; } - int v = -1; + PBVHVertRef v = {PBVH_REF_NONE}; + if (i == 0) { - v = index; + v = vertex; } else if (radius > 0.0f) { float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius; float location[3]; - flip_v3_v3(location, SCULPT_vertex_co_get(ss, index), i); + flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i); v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false); } - if (v != -1) { + if (v.i != PBVH_REF_NONE) { SCULPT_floodfill_add_initial(flood, v); } } @@ -1125,7 +1156,9 @@ void SCULPT_floodfill_add_active( if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { continue; } - int v = -1; + + PBVHVertRef v = {PBVH_REF_NONE}; + if (i == 0) { v = SCULPT_active_vertex_get(ss); } @@ -1135,26 +1168,31 @@ void SCULPT_floodfill_add_active( v = SCULPT_nearest_vertex_get(sd, ob, location, radius, false); } - if (v != -1) { + if (v.i != PBVH_REF_NONE) { SCULPT_floodfill_add_initial(flood, v); } } } -void SCULPT_floodfill_execute( - SculptSession *ss, - SculptFloodFill *flood, - bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata), - void *userdata) +void SCULPT_floodfill_execute(SculptSession *ss, + SculptFloodFill *flood, + bool (*func)(SculptSession *ss, + PBVHVertRef from_v, + PBVHVertRef to_v, + bool is_duplicate, + void *userdata), + void *userdata) { while (!BLI_gsqueue_is_empty(flood->queue)) { - int from_v; + PBVHVertRef from_v; + BLI_gsqueue_pop(flood->queue, &from_v); SculptVertexNeighborIter ni; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - const int to_v = ni.index; + const PBVHVertRef to_v = ni.vertex; + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); - if (BLI_BITMAP_TEST(flood->visited_vertices, to_v)) { + if (BLI_BITMAP_TEST(flood->visited_verts, to_v_i)) { continue; } @@ -1162,7 +1200,7 @@ void SCULPT_floodfill_execute( continue; } - BLI_BITMAP_ENABLE(flood->visited_vertices, to_v); + BLI_BITMAP_ENABLE(flood->visited_verts, BKE_pbvh_vertex_to_index(ss->pbvh, to_v)); if (func(ss, from_v, to_v, ni.is_duplicate, userdata)) { BLI_gsqueue_push(flood->queue, &to_v); @@ -1174,13 +1212,19 @@ void SCULPT_floodfill_execute( void SCULPT_floodfill_free(SculptFloodFill *flood) { - MEM_SAFE_FREE(flood->visited_vertices); + MEM_SAFE_FREE(flood->visited_verts); BLI_gsqueue_free(flood->queue); flood->queue = NULL; } /** \} */ +static bool sculpt_tool_has_cube_tip(const char sculpt_tool) +{ + return ELEM( + sculpt_tool, SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_PAINT, SCULPT_TOOL_MULTIPLANE_SCRAPE); +} + /* -------------------------------------------------------------------- */ /** \name Tool Capabilities * @@ -1282,10 +1326,13 @@ void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, Scul } } -void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node) +void SCULPT_orig_vert_data_init(SculptOrigVertData *data, + Object *ob, + PBVHNode *node, + SculptUndoType type) { SculptUndoNode *unode; - unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS); + unode = SCULPT_undo_push_node(ob, node, type); SCULPT_orig_vert_data_unode_init(data, ob, unode); } @@ -1359,16 +1406,13 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, switch (data->brush->sculpt_tool) { case SCULPT_TOOL_MASK: type = SCULPT_UNDO_MASK; - BKE_pbvh_node_mark_update_mask(data->nodes[n]); break; case SCULPT_TOOL_PAINT: case SCULPT_TOOL_SMEAR: type = SCULPT_UNDO_COLOR; - BKE_pbvh_node_mark_update_color(data->nodes[n]); break; default: type = SCULPT_UNDO_COORDS; - BKE_pbvh_node_mark_update(data->nodes[n]); break; } @@ -1376,13 +1420,27 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type); } else { - unode = SCULPT_undo_get_node(data->nodes[n]); + unode = SCULPT_undo_get_node(data->nodes[n], type); } if (!unode) { return; } + switch (type) { + case SCULPT_UNDO_MASK: + BKE_pbvh_node_mark_update_mask(data->nodes[n]); + break; + case SCULPT_UNDO_COLOR: + BKE_pbvh_node_mark_update_color(data->nodes[n]); + break; + case SCULPT_UNDO_COORDS: + BKE_pbvh_node_mark_update(data->nodes[n]); + break; + default: + break; + } + PBVHVertexIter vd; SculptOrigVertData orig_data; @@ -1399,16 +1457,15 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, else { copy_v3_v3(vd.fno, orig_data.no); } + if (vd.mvert) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } } else if (orig_data.unode->type == SCULPT_UNDO_MASK) { *vd.mask = orig_data.mask; } else if (orig_data.unode->type == SCULPT_UNDO_COLOR) { - SCULPT_vertex_color_set(ss, vd.index, orig_data.col); - } - - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + SCULPT_vertex_color_set(ss, vd.vertex, orig_data.col); } } BKE_pbvh_vertex_iter_end; @@ -1623,7 +1680,7 @@ bool SCULPT_brush_test_cube(SculptBrushTest *test, const float local[4][4], const float roundness) { - float side = M_SQRT1_2; + float side = 1.0f; float local_co[3]; if (sculpt_brush_test_clipping(test, co)) { @@ -2352,12 +2409,12 @@ static float brush_strength(const Sculpt *sd, float SCULPT_brush_strength_factor(SculptSession *ss, const Brush *br, const float brush_point[3], - const float len, + float len, const float vno[3], const float fno[3], - const float mask, - const int vertex_index, - const int thread_id) + float mask, + const PBVHVertRef vertex, + int thread_id) { StrokeCache *cache = ss->cache; const Scene *scene = cache->vc->scene; @@ -2375,7 +2432,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss, /* 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); } - else if (ss->texcache) { + else { float symm_point[3], point_2d[2]; /* Quite warnings. */ float x = 0.0f, y = 0.0f; @@ -2441,7 +2498,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss, avg *= 1.0f - mask; /* Auto-masking. */ - avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex_index); + avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex); return avg; } @@ -2810,7 +2867,7 @@ typedef struct { float depth; bool original; - int active_vertex_index; + PBVHVertRef active_vertex; float *face_normal; int active_face_grid_index; @@ -3030,13 +3087,13 @@ static void do_gravity_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -3105,12 +3162,12 @@ void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) /* Modifying of basis key should update mesh. */ if (kb == me->key->refkey) { - MVert *mvert = me->mvert; + MVert *verts = BKE_mesh_verts_for_write(me); - for (a = 0; a < me->totvert; a++, mvert++) { - copy_v3_v3(mvert->co, vertCos[a]); + for (a = 0; a < me->totvert; a++) { + copy_v3_v3(verts[a].co, vertCos[a]); } - BKE_mesh_normals_tag_dirty(me); + BKE_mesh_tag_coords_changed(me); } /* Apply new coords on active key block, no need to re-allocate kb->data here! */ @@ -3196,24 +3253,29 @@ static void do_brush_action_task_cb(void *__restrict userdata, SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; + bool need_coords = ss->cache->supports_gravity; + /* Face Sets modifications do a single undo push */ if (data->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) { BKE_pbvh_node_mark_redraw(data->nodes[n]); /* Draw face sets in smooth mode moves the vertices. */ if (ss->cache->alt_smooth) { - SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); - BKE_pbvh_node_mark_update(data->nodes[n]); + need_coords = true; } } else if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); BKE_pbvh_node_mark_update_mask(data->nodes[n]); } - else if (SCULPT_TOOL_NEEDS_COLOR(data->brush->sculpt_tool)) { + else if (SCULPT_tool_is_paint(data->brush->sculpt_tool)) { SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR); BKE_pbvh_node_mark_update_color(data->nodes[n]); } else { + need_coords = true; + } + + if (need_coords) { SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_node_mark_update(data->nodes[n]); } @@ -3232,7 +3294,7 @@ static void do_brush_action(Sculpt *sd, /* Check for unsupported features. */ PBVHType type = BKE_pbvh_type(ss->pbvh); - if (SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) { + if (SCULPT_tool_is_paint(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) { if (type != PBVH_FACES) { return; } @@ -3254,6 +3316,11 @@ static void do_brush_action(Sculpt *sd, ss->cache->original; float radius_scale = 1.0f; + /* Corners of square brushes can go outside the brush radius. */ + if (sculpt_tool_has_cube_tip(brush->sculpt_tool)) { + radius_scale = M_SQRT2; + } + /* With these options enabled not all required nodes are inside the original brush radius, so * the brush can produce artifacts in some situations. */ if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) { @@ -3343,7 +3410,7 @@ static void do_brush_action(Sculpt *sd, if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) { if (!ss->cache->cloth_sim) { ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create( - ss, 1.0f, 0.0f, 0.0f, false, true); + ob, 1.0f, 0.0f, 0.0f, false, true); SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim); } SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim); @@ -3522,8 +3589,9 @@ static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd) copy_v3_v3(ss->deform_cos[index], vd->co); copy_v3_v3(ss->orig_cos[index], newco); + MVert *verts = BKE_mesh_verts_for_write(me); if (!ss->shapekey_active) { - copy_v3_v3(me->mvert[index].co, newco); + copy_v3_v3(verts[index].co, newco); } } @@ -3926,27 +3994,6 @@ static void do_symmetrical_brush_actions(Sculpt *sd, } } -static void sculpt_update_tex(const Scene *scene, Sculpt *sd, SculptSession *ss) -{ - Brush *brush = BKE_paint_brush(&sd->paint); - const int radius = BKE_brush_size_get(scene, brush); - - MEM_SAFE_FREE(ss->texcache); - - if (ss->tex_pool) { - BKE_image_pool_free(ss->tex_pool); - ss->tex_pool = NULL; - } - - /* Need to allocate a bigger buffer for bigger brush size. */ - ss->texcache_side = 2 * radius; - if (!ss->texcache || ss->texcache_side > ss->texcache_actual) { - ss->texcache = BKE_brush_gen_texture_cache(brush, radius, false); - ss->texcache_actual = ss->texcache_side; - ss->tex_pool = BKE_image_pool_new(); - } -} - bool SCULPT_mode_poll(bContext *C) { Object *ob = CTX_data_active_object(C); @@ -4338,13 +4385,16 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo * generally used to create grab deformations. */ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) { + if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && (brush->flag & BRUSH_ANCHORED)) { + return true; + } + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_POSE, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_THUMB, - SCULPT_TOOL_ELASTIC_DEFORM, - SCULPT_TOOL_SMEAR)) { + SCULPT_TOOL_ELASTIC_DEFORM)) { return true; } if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && @@ -4692,8 +4742,8 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd, (brush->sculpt_tool == SCULPT_TOOL_POSE) || (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) || (brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) || - SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) || - (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) || + SCULPT_tool_is_paint(brush->sculpt_tool) || (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || + (brush->sculpt_tool == SCULPT_TOOL_SMEAR) || (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) || (brush->sculpt_tool == SCULPT_TOOL_DISPLACEMENT_SMEAR) || (brush->sculpt_tool == SCULPT_TOOL_PAINT)); @@ -4709,7 +4759,8 @@ void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *b if (ss->shapekey_active || ss->deform_modifiers_active || (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false, false); + BKE_sculpt_update_object_for_edit( + depsgraph, ob, need_pmap, false, SCULPT_tool_is_paint(brush->sculpt_tool)); } } @@ -4728,7 +4779,7 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) } else { /* Intersect with coordinates from before we started stroke. */ - SculptUndoNode *unode = SCULPT_undo_get_node(node); + SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS); origco = (unode) ? unode->co : NULL; use_origco = origco ? true : false; } @@ -4742,7 +4793,7 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) srd->ray_normal, &srd->isect_precalc, &srd->depth, - &srd->active_vertex_index, + &srd->active_vertex, &srd->active_face_grid_index, srd->face_normal)) { srd->hit = true; @@ -4765,7 +4816,7 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t } else { /* Intersect with coordinates from before we started stroke. */ - SculptUndoNode *unode = SCULPT_undo_get_node(node); + SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS); origco = (unode) ? unode->co : NULL; use_origco = origco ? true : false; } @@ -4876,7 +4927,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, } /* Update the active vertex of the SculptSession. */ - ss->active_vertex_index = srd.active_vertex_index; + ss->active_vertex = srd.active_vertex; SCULPT_vertex_random_access_ensure(ss); copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss)); @@ -4950,7 +5001,10 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, return true; } -bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) +bool SCULPT_stroke_get_location(bContext *C, + float out[3], + const float mval[2], + bool force_original) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob; @@ -4966,7 +5020,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) ss = ob->sculpt; cache = ss->cache; - original = (cache) ? cache->original : false; + original = force_original || ((cache) ? cache->original : false); const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); @@ -5029,7 +5083,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) return hit; } -static void sculpt_brush_init_tex(const Scene *scene, Sculpt *sd, SculptSession *ss) +static void sculpt_brush_init_tex(Sculpt *sd, SculptSession *ss) { Brush *brush = BKE_paint_brush(&sd->paint); MTex *mtex = &brush->mtex; @@ -5040,16 +5094,16 @@ static void sculpt_brush_init_tex(const Scene *scene, Sculpt *sd, SculptSession ntreeTexBeginExecTree(mtex->tex->nodetree); } - /* TODO: Shouldn't really have to do this at the start of every stroke, but sculpt would need - * some sort of notification when changes are made to the texture. */ - sculpt_update_tex(scene, sd, ss); + if (ss->tex_pool == NULL) { + ss->tex_pool = BKE_image_pool_new(); + } } static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + ToolSettings *tool_settings = CTX_data_tool_settings(C); + Sculpt *sd = tool_settings->sculpt; SculptSession *ss = CTX_data_active_object(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); int mode = RNA_enum_get(op->ptr, "mode"); @@ -5066,10 +5120,11 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) } view3d_operator_needs_opengl(C); - sculpt_brush_init_tex(scene, sd, ss); + sculpt_brush_init_tex(sd, ss); need_pmap = sculpt_needs_connectivity_info(sd, brush, ss, mode); - needs_colors = SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool); + needs_colors = SCULPT_tool_is_paint(brush->sculpt_tool) && + !SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob); if (needs_colors) { BKE_sculpt_color_layer_create_if_needed(ob); @@ -5078,7 +5133,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of * earlier steps modifying the data. */ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, need_mask, needs_colors); + BKE_sculpt_update_object_for_edit( + depsgraph, ob, need_pmap, need_mask, SCULPT_tool_is_paint(brush->sculpt_tool)); ED_paint_tool_update_sticky_shading_color(C, ob); } @@ -5280,7 +5336,7 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2]) { float co_dummy[3]; - return SCULPT_stroke_get_location(C, co_dummy, mval); + return SCULPT_stroke_get_location(C, co_dummy, mval, false); } bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) @@ -5316,7 +5372,8 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f /* NOTE: This should be removed when paint mode is available. Paint mode can force based on the * canvas it is painting on. (ref. use_sculpt_texture_paint). */ - if (brush && SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) { + if (brush && SCULPT_tool_is_paint(brush->sculpt_tool) && + !SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { View3D *v3d = CTX_wm_view3d(C); if (v3d->shading.type == OB_SOLID) { v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; @@ -5337,7 +5394,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f ED_image_undo_push_begin(op->type->name, PAINT_MODE_SCULPT); } else { - SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); + SCULPT_undo_push_begin_ex(ob, sculpt_tool_name(sd)); } return true; @@ -5347,7 +5404,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f static void sculpt_stroke_update_step(bContext *C, wmOperator *UNUSED(op), - struct PaintStroke *UNUSED(stroke), + struct PaintStroke *stroke, PointerRNA *itemptr) { UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; @@ -5356,6 +5413,8 @@ static void sculpt_stroke_update_step(bContext *C, SculptSession *ss = ob->sculpt; const Brush *brush = BKE_paint_brush(&sd->paint); ToolSettings *tool_settings = CTX_data_tool_settings(C); + StrokeCache *cache = ss->cache; + cache->stroke_distance = paint_stroke_distance_get(stroke); SCULPT_stroke_modifiers_check(C, ob, brush); sculpt_update_cache_variants(C, sd, ob, itemptr); @@ -5408,7 +5467,7 @@ static void sculpt_stroke_update_step(bContext *C, if (brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); } - else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) { + else if (SCULPT_tool_is_paint(brush->sculpt_tool)) { if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { SCULPT_flush_update_step(C, SCULPT_UPDATE_IMAGE); } @@ -5494,14 +5553,27 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent struct PaintStroke *stroke; int ignore_background_click; int retval; + Object *ob = CTX_data_active_object(C); + + /* Test that ob is visible; otherwise we won't be able to get evaluated data + * from the depsgraph. We do this here instead of SCULPT_mode_poll + * to avoid falling through to the translate operator in the + * global view3d keymap. + * + * NOTE: #BKE_object_is_visible_in_viewport is not working here (it returns false + * if the object is in local view); instead, test for OB_HIDE_VIEWPORT directly. + */ + + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { + return OPERATOR_CANCELLED; + } sculpt_brush_stroke_init(C, op); - Object *ob = CTX_data_active_object(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - if (SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) && + if (SCULPT_tool_is_paint(brush->sculpt_tool) && !SCULPT_handles_colors_report(ob->sculpt, op->reports)) { return OPERATOR_CANCELLED; } @@ -5585,6 +5657,10 @@ static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent return paint_stroke_modal(C, op, event, (struct PaintStroke **)&op->customdata); } +static void sculpt_redo_empty_ui(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ +} + void SCULPT_OT_brush_stroke(wmOperatorType *ot) { /* Identifiers. */ @@ -5598,9 +5674,10 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot) ot->exec = sculpt_brush_stroke_exec; ot->poll = SCULPT_poll; ot->cancel = sculpt_brush_stroke_cancel; + ot->ui = sculpt_redo_empty_ui; /* Flags (sculpt does own undo? (ton)). */ - ot->flag = OPTYPE_BLOCKING; + ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO; /* Properties. */ @@ -5640,10 +5717,10 @@ enum { SCULPT_TOPOLOGY_ID_DEFAULT, }; -static int SCULPT_vertex_get_connected_component(SculptSession *ss, int index) +static int SCULPT_vertex_get_connected_component(SculptSession *ss, PBVHVertRef vertex) { if (ss->vertex_info.connected_component) { - return ss->vertex_info.connected_component[index]; + return ss->vertex_info.connected_component[vertex.i]; } return SCULPT_TOPOLOGY_ID_DEFAULT; } @@ -5660,8 +5737,11 @@ static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist) ss->fake_neighbors.current_max_distance = max_dist; } -static void SCULPT_fake_neighbor_add(SculptSession *ss, int v_index_a, int v_index_b) +static void SCULPT_fake_neighbor_add(SculptSession *ss, PBVHVertRef v_a, PBVHVertRef v_b) { + int v_index_a = BKE_pbvh_vertex_to_index(ss->pbvh, v_a); + int v_index_b = BKE_pbvh_vertex_to_index(ss->pbvh, v_b); + if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) { ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b; ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a; @@ -5674,7 +5754,7 @@ static void sculpt_pose_fake_neighbors_free(SculptSession *ss) } typedef struct NearestVertexFakeNeighborTLSData { - int nearest_vertex_index; + PBVHVertRef nearest_vertex; float nearest_vertex_distance_squared; int current_topology_id; } NearestVertexFakeNeighborTLSData; @@ -5689,13 +5769,13 @@ static void do_fake_neighbor_search_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.index); + int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.vertex); if (vd_topology_id != nvtd->current_topology_id && ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) { float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); if (distance_squared < nvtd->nearest_vertex_distance_squared && distance_squared < data->max_distance_squared) { - nvtd->nearest_vertex_index = vd.index; + nvtd->nearest_vertex = vd.vertex; nvtd->nearest_vertex_distance_squared = distance_squared; } } @@ -5709,17 +5789,20 @@ static void fake_neighbor_search_reduce(const void *__restrict UNUSED(userdata), { NearestVertexFakeNeighborTLSData *join = chunk_join; NearestVertexFakeNeighborTLSData *nvtd = chunk; - if (join->nearest_vertex_index == -1) { - join->nearest_vertex_index = nvtd->nearest_vertex_index; + if (join->nearest_vertex.i == PBVH_REF_NONE) { + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { - join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } } -static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, float max_distance) +static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd, + Object *ob, + const PBVHVertRef vertex, + float max_distance) { SculptSession *ss = ob->sculpt; PBVHNode **nodes = NULL; @@ -5729,12 +5812,12 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, .sd = sd, .radius_squared = max_distance * max_distance, .original = false, - .center = SCULPT_vertex_co_get(ss, index), + .center = SCULPT_vertex_co_get(ss, vertex), }; BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); if (totnode == 0) { - return -1; + return BKE_pbvh_make_vref(PBVH_REF_NONE); } SculptThreadedTaskData task_data = { @@ -5744,12 +5827,12 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, .max_distance_squared = max_distance * max_distance, }; - copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, vertex)); NearestVertexFakeNeighborTLSData nvtd; - nvtd.nearest_vertex_index = -1; + nvtd.nearest_vertex.i = -1; nvtd.nearest_vertex_distance_squared = FLT_MAX; - nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, index); + nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, vertex); TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -5760,19 +5843,26 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, MEM_SAFE_FREE(nodes); - return nvtd.nearest_vertex_index; + return nvtd.nearest_vertex; } typedef struct SculptTopologyIDFloodFillData { int next_id; } SculptTopologyIDFloodFillData; -static bool SCULPT_connected_components_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata) +static bool SCULPT_connected_components_floodfill_cb(SculptSession *ss, + PBVHVertRef from_v, + PBVHVertRef to_v, + bool UNUSED(is_duplicate), + void *userdata) { SculptTopologyIDFloodFillData *data = userdata; - ss->vertex_info.connected_component[from_v] = data->next_id; - ss->vertex_info.connected_component[to_v] = data->next_id; + + 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); + + ss->vertex_info.connected_component[from_v_i] = data->next_id; + ss->vertex_info.connected_component[to_v_i] = data->next_id; return true; } @@ -5796,10 +5886,12 @@ void SCULPT_connected_components_ensure(Object *ob) int next_id = 0; for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (ss->vertex_info.connected_component[i] == SCULPT_TOPOLOGY_ID_NONE) { SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); - SCULPT_floodfill_add_initial(&flood, i); + SCULPT_floodfill_add_initial(&flood, vertex); SculptTopologyIDFloodFillData data; data.next_id = next_id; SCULPT_floodfill_execute(ss, &flood, SCULPT_connected_components_floodfill_cb, &data); @@ -5817,21 +5909,25 @@ void SCULPT_boundary_info_ensure(Object *object) } Mesh *base_mesh = BKE_mesh_from_object(object); + const MEdge *edges = BKE_mesh_edges(base_mesh); + const MPoly *polys = BKE_mesh_polys(base_mesh); + const MLoop *loops = BKE_mesh_loops(base_mesh); + ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info"); int *adjacent_faces_edge_count = MEM_calloc_arrayN( base_mesh->totedge, sizeof(int), "Adjacent face edge count"); for (int p = 0; p < base_mesh->totpoly; p++) { - MPoly *poly = &base_mesh->mpoly[p]; + const MPoly *poly = &polys[p]; for (int l = 0; l < poly->totloop; l++) { - MLoop *loop = &base_mesh->mloop[l + poly->loopstart]; + const MLoop *loop = &loops[l + poly->loopstart]; adjacent_faces_edge_count[loop->e]++; } } for (int e = 0; e < base_mesh->totedge; e++) { if (adjacent_faces_edge_count[e] < 2) { - MEdge *edge = &base_mesh->medge[e]; + const MEdge *edge = &edges[e]; BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true); BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true); } @@ -5857,12 +5953,12 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) SCULPT_fake_neighbor_init(ss, max_dist); for (int i = 0; i < totvert; i++) { - const int from_v = i; + const PBVHVertRef from_v = BKE_pbvh_index_to_vertex(ss->pbvh, i); /* This vertex does not have a fake neighbor yet, search one for it. */ - if (ss->fake_neighbors.fake_neighbor_index[from_v] == FAKE_NEIGHBOR_NONE) { - const int to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist); - if (to_v != -1) { + if (ss->fake_neighbors.fake_neighbor_index[i] == FAKE_NEIGHBOR_NONE) { + const PBVHVertRef to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist); + if (to_v.i != PBVH_REF_NONE) { /* Add the fake neighbor if available. */ SCULPT_fake_neighbor_add(ss, from_v, to_v); } diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index bb101717c9b..a9fe8cc4b2f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -114,16 +114,21 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush return false; } -float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert) +float SCULPT_automasking_factor_get(AutomaskingCache *automasking, + SculptSession *ss, + PBVHVertRef vert) { if (!automasking) { 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[vert]; + return automasking->factor[index]; } if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { @@ -178,13 +183,18 @@ struct AutomaskFloodFillData { char symm; }; -static bool automask_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata) +static bool automask_floodfill_cb(SculptSession *ss, + PBVHVertRef from_v, + PBVHVertRef to_v, + bool UNUSED(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] = 1.0f; - data->automask_factor[from_v] = 1.0f; + data->automask_factor[to_v_i] = 1.0f; + data->automask_factor[from_v_i] = 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)); @@ -243,7 +253,9 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a int tot_vert = SCULPT_vertex_count_get(ss); int active_face_set = SCULPT_active_face_set_get(ss); for (int i : IndexRange(tot_vert)) { - if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + 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; } } @@ -269,15 +281,17 @@ float *SCULPT_boundary_automasking_init(Object *ob, int *edge_distance = (int *)MEM_callocN(sizeof(int) * totvert, "automask_factor"); for (int i : IndexRange(totvert)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + edge_distance[i] = EDGE_DISTANCE_INF; switch (mode) { case AUTOMASK_INIT_BOUNDARY_EDGES: - if (SCULPT_vertex_is_boundary(ss, i)) { + if (SCULPT_vertex_is_boundary(ss, vertex)) { edge_distance[i] = 0; } break; case AUTOMASK_INIT_BOUNDARY_FACE_SETS: - if (!SCULPT_vertex_has_unique_face_set(ss, i)) { + if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) { edge_distance[i] = 0; } break; @@ -286,11 +300,13 @@ float *SCULPT_boundary_automasking_init(Object *ob, for (int propagation_it : IndexRange(propagation_steps)) { for (int i : IndexRange(totvert)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (edge_distance[i] != EDGE_DISTANCE_INF) { continue; } SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { if (edge_distance[ni.index] == propagation_it) { edge_distance[i] = propagation_it + 1; } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index 8bf09ce3d05..005892b88a0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -46,32 +46,38 @@ #define BOUNDARY_STEPS_NONE -1 typedef struct BoundaryInitialVertexFloodFillData { - int initial_vertex; + PBVHVertRef initial_vertex; + int initial_vertex_i; int boundary_initial_vertex_steps; - int boundary_initial_vertex; + PBVHVertRef boundary_initial_vertex; + int boundary_initial_vertex_i; int *floodfill_steps; float radius_sq; } BoundaryInitialVertexFloodFillData; static bool boundary_initial_vertex_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { BoundaryInitialVertexFloodFillData *data = 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); + if (!SCULPT_vertex_visible_get(ss, to_v)) { return false; } if (!is_duplicate) { - data->floodfill_steps[to_v] = data->floodfill_steps[from_v] + 1; + data->floodfill_steps[to_v_i] = data->floodfill_steps[from_v_i] + 1; } else { - data->floodfill_steps[to_v] = data->floodfill_steps[from_v]; + data->floodfill_steps[to_v_i] = data->floodfill_steps[from_v_i]; } if (SCULPT_vertex_is_boundary(ss, to_v)) { - if (data->floodfill_steps[to_v] < data->boundary_initial_vertex_steps) { - data->boundary_initial_vertex_steps = data->floodfill_steps[to_v]; + if (data->floodfill_steps[to_v_i] < data->boundary_initial_vertex_steps) { + data->boundary_initial_vertex_steps = data->floodfill_steps[to_v_i]; + data->boundary_initial_vertex_i = to_v_i; data->boundary_initial_vertex = to_v; } } @@ -83,9 +89,9 @@ static bool boundary_initial_vertex_floodfill_cb( /* From a vertex index anywhere in the mesh, returns the closest vertex in a mesh boundary inside * the given radius, if it exists. */ -static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, - const int initial_vertex, - const float radius) +static PBVHVertRef sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, + const PBVHVertRef initial_vertex, + const float radius) { if (SCULPT_vertex_is_boundary(ss, initial_vertex)) { @@ -98,7 +104,7 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, BoundaryInitialVertexFloodFillData fdata = { .initial_vertex = initial_vertex, - .boundary_initial_vertex = BOUNDARY_VERTEX_NONE, + .boundary_initial_vertex = {BOUNDARY_VERTEX_NONE}, .boundary_initial_vertex_steps = INT_MAX, .radius_sq = radius * radius, }; @@ -119,34 +125,38 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, static int BOUNDARY_INDICES_BLOCK_SIZE = 300; static void sculpt_boundary_index_add(SculptBoundary *boundary, + const PBVHVertRef new_vertex, const int new_index, const float distance, - GSet *included_vertices) + GSet *included_verts) { - boundary->vertices[boundary->num_vertices] = new_index; + boundary->verts[boundary->verts_num] = new_vertex; + if (boundary->distance) { boundary->distance[new_index] = distance; } - if (included_vertices) { - BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index)); + if (included_verts) { + BLI_gset_add(included_verts, POINTER_FROM_INT(new_index)); } - boundary->num_vertices++; - if (boundary->num_vertices >= boundary->vertices_capacity) { - boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE; - boundary->vertices = MEM_reallocN_id( - boundary->vertices, boundary->vertices_capacity * sizeof(int), "boundary indices"); + boundary->verts_num++; + if (boundary->verts_num >= boundary->verts_capacity) { + boundary->verts_capacity += BOUNDARY_INDICES_BLOCK_SIZE; + boundary->verts = MEM_reallocN_id( + boundary->verts, boundary->verts_capacity * sizeof(PBVHVertRef), "boundary indices"); } }; -static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int v1, const int v2) +static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, + const PBVHVertRef v1, + const PBVHVertRef v2) { - boundary->edges[boundary->num_edges].v1 = v1; - boundary->edges[boundary->num_edges].v2 = v2; - boundary->num_edges++; + boundary->edges[boundary->edges_num].v1 = v1; + boundary->edges[boundary->edges_num].v2 = v2; + boundary->edges_num++; - if (boundary->num_edges >= boundary->edges_capacity) { + if (boundary->edges_num >= boundary->edges_capacity) { boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE; boundary->edges = MEM_reallocN_id(boundary->edges, boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge), @@ -159,7 +169,7 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int * as well as to check if the initial vertex is valid. */ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, - const int initial_vertex) + const PBVHVertRef initial_vertex) { if (!SCULPT_vertex_visible_get(ss, initial_vertex)) { @@ -170,9 +180,9 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, int boundary_vertex_count = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vertex, ni) { - if (SCULPT_vertex_visible_get(ss, ni.index)) { + if (SCULPT_vertex_visible_get(ss, ni.vertex)) { neighbor_count++; - if (SCULPT_vertex_is_boundary(ss, ni.index)) { + if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { boundary_vertex_count++; } } @@ -199,16 +209,19 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, typedef struct BoundaryFloodFillData { SculptBoundary *boundary; - GSet *included_vertices; + GSet *included_verts; EdgeSet *preview_edges; - int last_visited_vertex; + PBVHVertRef last_visited_vertex; } BoundaryFloodFillData; static bool boundary_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *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); + BoundaryFloodFillData *data = userdata; SculptBoundary *boundary = data->boundary; if (!SCULPT_vertex_is_boundary(ss, to_v)) { @@ -217,9 +230,10 @@ static bool boundary_floodfill_cb( const float edge_len = len_v3v3(SCULPT_vertex_co_get(ss, from_v), SCULPT_vertex_co_get(ss, to_v)); const float distance_boundary_to_dst = boundary->distance ? - boundary->distance[from_v] + edge_len : + boundary->distance[from_v_i] + edge_len : 0.0f; - sculpt_boundary_index_add(boundary, to_v, distance_boundary_to_dst, data->included_vertices); + sculpt_boundary_index_add( + boundary, to_v, to_v_i, distance_boundary_to_dst, data->included_verts); if (!is_duplicate) { sculpt_boundary_preview_edge_add(boundary, from_v, to_v); } @@ -229,32 +243,38 @@ static bool boundary_floodfill_cb( static void sculpt_boundary_indices_init(SculptSession *ss, SculptBoundary *boundary, const bool init_boundary_distances, - const int initial_boundary_index) + const PBVHVertRef initial_boundary_vertex) { const int totvert = SCULPT_vertex_count_get(ss); - boundary->vertices = MEM_malloc_arrayN( - BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices"); + boundary->verts = MEM_malloc_arrayN( + BOUNDARY_INDICES_BLOCK_SIZE, sizeof(PBVHVertRef), "boundary indices"); + if (init_boundary_distances) { boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances"); } boundary->edges = MEM_malloc_arrayN( BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges"); - GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE); + GSet *included_verts = BLI_gset_int_new_ex("included verts", BOUNDARY_INDICES_BLOCK_SIZE); SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); - boundary->initial_vertex = initial_boundary_index; + int initial_boundary_index = BKE_pbvh_vertex_to_index(ss->pbvh, initial_boundary_vertex); + + boundary->initial_vertex = initial_boundary_vertex; + boundary->initial_vertex_i = initial_boundary_index; + copy_v3_v3(boundary->initial_vertex_position, SCULPT_vertex_co_get(ss, boundary->initial_vertex)); - sculpt_boundary_index_add(boundary, initial_boundary_index, 0.0f, included_vertices); - SCULPT_floodfill_add_initial(&flood, initial_boundary_index); + sculpt_boundary_index_add( + boundary, initial_boundary_vertex, initial_boundary_index, 0.0f, included_verts); + SCULPT_floodfill_add_initial(&flood, boundary->initial_vertex); BoundaryFloodFillData fdata = { .boundary = boundary, - .included_vertices = included_vertices, - .last_visited_vertex = BOUNDARY_VERTEX_NONE, + .included_verts = included_verts, + .last_visited_vertex = {BOUNDARY_VERTEX_NONE}, }; @@ -262,20 +282,20 @@ static void sculpt_boundary_indices_init(SculptSession *ss, SCULPT_floodfill_free(&flood); /* Check if the boundary loops into itself and add the extra preview edge to close the loop. */ - if (fdata.last_visited_vertex != BOUNDARY_VERTEX_NONE && + if (fdata.last_visited_vertex.i != BOUNDARY_VERTEX_NONE && sculpt_boundary_is_vertex_in_editable_boundary(ss, fdata.last_visited_vertex)) { SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) { - if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) && - sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.index)) { - sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.index); + if (BLI_gset_haskey(included_verts, POINTER_FROM_INT(ni.index)) && + sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.vertex)) { + sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.vertex); boundary->forms_loop = true; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); } - BLI_gset_free(included_vertices, NULL); + BLI_gset_free(included_verts, NULL); } /** @@ -286,7 +306,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss, */ static void sculpt_boundary_edit_data_init(SculptSession *ss, SculptBoundary *boundary, - const int initial_vertex, + const PBVHVertRef initial_vertex, const float radius) { const int totvert = SCULPT_vertex_count_get(ss); @@ -297,72 +317,78 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info"); for (int i = 0; i < totvert; i++) { - boundary->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE; - boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE; + boundary->edit_info[i].original_vertex_i = BOUNDARY_VERTEX_NONE; + boundary->edit_info[i].propagation_steps_num = BOUNDARY_STEPS_NONE; } - GSQueue *current_iteration = BLI_gsqueue_new(sizeof(int)); - GSQueue *next_iteration = BLI_gsqueue_new(sizeof(int)); + GSQueue *current_iteration = BLI_gsqueue_new(sizeof(PBVHVertRef)); + GSQueue *next_iteration = BLI_gsqueue_new(sizeof(PBVHVertRef)); /* Initialized the first iteration with the vertices already in the boundary. This is propagation * step 0. */ - BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices"); - for (int i = 0; i < boundary->num_vertices; i++) { - boundary->edit_info[boundary->vertices[i]].original_vertex = boundary->vertices[i]; - boundary->edit_info[boundary->vertices[i]].num_propagation_steps = 0; + BLI_bitmap *visited_verts = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_verts"); + for (int i = 0; i < boundary->verts_num; i++) { + int index = BKE_pbvh_vertex_to_index(ss->pbvh, boundary->verts[i]); + + boundary->edit_info[index].original_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, + boundary->verts[i]); + boundary->edit_info[index].propagation_steps_num = 0; /* This ensures that all duplicate vertices in the boundary have the same original_vertex * index, so the deformation for them will be the same. */ if (has_duplicates) { SculptVertexNeighborIter ni_duplis; - SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) { + SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->verts[i], ni_duplis) { if (ni_duplis.is_duplicate) { - boundary->edit_info[ni_duplis.index].original_vertex = boundary->vertices[i]; + boundary->edit_info[ni_duplis.index].original_vertex_i = BKE_pbvh_vertex_to_index( + ss->pbvh, boundary->verts[i]); } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); } - BLI_gsqueue_push(current_iteration, &boundary->vertices[i]); + BLI_gsqueue_push(current_iteration, &boundary->verts[i]); } - int num_propagation_steps = 0; + int propagation_steps_num = 0; float accum_distance = 0.0f; while (true) { /* Stop adding steps to edit info. This happens when a steps is further away from the boundary * than the brush radius or when the entire mesh was already processed. */ if (accum_distance > radius || BLI_gsqueue_is_empty(current_iteration)) { - boundary->max_propagation_steps = num_propagation_steps; + boundary->max_propagation_steps = propagation_steps_num; break; } while (!BLI_gsqueue_is_empty(current_iteration)) { - int from_v; + PBVHVertRef from_v; BLI_gsqueue_pop(current_iteration, &from_v); + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + SculptVertexNeighborIter ni; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - const bool is_visible = SCULPT_vertex_visible_get(ss, ni.index); + const bool is_visible = SCULPT_vertex_visible_get(ss, ni.vertex); if (!is_visible || - boundary->edit_info[ni.index].num_propagation_steps != BOUNDARY_STEPS_NONE) { + boundary->edit_info[ni.index].propagation_steps_num != BOUNDARY_STEPS_NONE) { continue; } - boundary->edit_info[ni.index].original_vertex = - boundary->edit_info[from_v].original_vertex; + boundary->edit_info[ni.index].original_vertex_i = + boundary->edit_info[from_v_i].original_vertex_i; - BLI_BITMAP_ENABLE(visited_vertices, ni.index); + BLI_BITMAP_ENABLE(visited_verts, ni.index); if (ni.is_duplicate) { /* Grids duplicates handling. */ - boundary->edit_info[ni.index].num_propagation_steps = - boundary->edit_info[from_v].num_propagation_steps; + boundary->edit_info[ni.index].propagation_steps_num = + boundary->edit_info[from_v_i].propagation_steps_num; } else { - boundary->edit_info[ni.index].num_propagation_steps = - boundary->edit_info[from_v].num_propagation_steps + 1; + boundary->edit_info[ni.index].propagation_steps_num = + boundary->edit_info[from_v_i].propagation_steps_num + 1; - BLI_gsqueue_push(next_iteration, &ni.index); + BLI_gsqueue_push(next_iteration, &ni.vertex); /* When copying the data to the neighbor for the next iteration, it has to be copied to * all its duplicates too. This is because it is not possible to know if the updated @@ -370,12 +396,12 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, * copy the data in the from_v neighbor iterator. */ if (has_duplicates) { SculptVertexNeighborIter ni_duplis; - SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.index, ni_duplis) { + SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni_duplis) { if (ni_duplis.is_duplicate) { - boundary->edit_info[ni_duplis.index].original_vertex = - boundary->edit_info[from_v].original_vertex; - boundary->edit_info[ni_duplis.index].num_propagation_steps = - boundary->edit_info[from_v].num_propagation_steps + 1; + boundary->edit_info[ni_duplis.index].original_vertex_i = + boundary->edit_info[from_v_i].original_vertex_i; + boundary->edit_info[ni_duplis.index].propagation_steps_num = + boundary->edit_info[from_v_i].propagation_steps_num + 1; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); @@ -383,11 +409,12 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* Check the distance using the vertex that was propagated from the initial vertex that * was used to initialize the boundary. */ - if (boundary->edit_info[from_v].original_vertex == initial_vertex) { - boundary->pivot_vertex = ni.index; - copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index)); + if (boundary->edit_info[from_v_i].original_vertex_i == + BKE_pbvh_vertex_to_index(ss->pbvh, initial_vertex)) { + boundary->pivot_vertex = ni.vertex; + copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.vertex)); accum_distance += len_v3v3(SCULPT_vertex_co_get(ss, from_v), - SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_vertex_co_get(ss, ni.vertex)); } } } @@ -396,15 +423,15 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* Copy the new vertices to the queue to be processed in the next iteration. */ while (!BLI_gsqueue_is_empty(next_iteration)) { - int next_v; + PBVHVertRef next_v; BLI_gsqueue_pop(next_iteration, &next_v); BLI_gsqueue_push(current_iteration, &next_v); } - num_propagation_steps++; + propagation_steps_num++; } - MEM_SAFE_FREE(visited_vertices); + MEM_SAFE_FREE(visited_verts); BLI_gsqueue_free(current_iteration); BLI_gsqueue_free(next_iteration); @@ -422,12 +449,13 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, BKE_curvemapping_init(brush->curve); for (int i = 0; i < totvert; i++) { - if (boundary->edit_info[i].num_propagation_steps != -1) { + if (boundary->edit_info[i].propagation_steps_num != -1) { boundary->edit_info[i].strength_factor = BKE_brush_curve_strength( - brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps); + brush, boundary->edit_info[i].propagation_steps_num, boundary->max_propagation_steps); } - if (boundary->edit_info[i].original_vertex == boundary->initial_vertex) { + if (boundary->edit_info[i].original_vertex_i == + BKE_pbvh_vertex_to_index(ss->pbvh, boundary->initial_vertex)) { /* All vertices that are propagated from the original vertex won't be affected by the * boundary falloff, so there is no need to calculate anything else. */ continue; @@ -439,7 +467,7 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, continue; } - const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex]; + const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex_i]; float falloff_distance = 0.0f; float direction = 1.0f; @@ -473,22 +501,22 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, SculptBoundary *SCULPT_boundary_data_init(Object *object, Brush *brush, - const int initial_vertex, + const PBVHVertRef initial_vertex, const float radius) { SculptSession *ss = object->sculpt; - if (initial_vertex == BOUNDARY_VERTEX_NONE) { + if (initial_vertex.i == PBVH_REF_NONE) { return NULL; } SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(object); - const int boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex( + const PBVHVertRef boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex( ss, initial_vertex, radius); - if (boundary_initial_vertex == BOUNDARY_VERTEX_NONE) { + if (boundary_initial_vertex.i == BOUNDARY_VERTEX_NONE) { return NULL; } @@ -514,7 +542,7 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object, void SCULPT_boundary_data_free(SculptBoundary *boundary) { - MEM_SAFE_FREE(boundary->vertices); + MEM_SAFE_FREE(boundary->verts); MEM_SAFE_FREE(boundary->edges); MEM_SAFE_FREE(boundary->distance); MEM_SAFE_FREE(boundary->edit_info); @@ -536,30 +564,35 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo boundary->bend.pivot_positions = MEM_calloc_arrayN(totvert, sizeof(float[3]), "pivot positions"); for (int i = 0; i < totvert; i++) { - if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { + if (boundary->edit_info[i].propagation_steps_num != boundary->max_propagation_steps) { continue; } + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float dir[3]; float normal[3]; - SCULPT_vertex_normal_get(ss, i, normal); - sub_v3_v3v3(dir, - SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), - SCULPT_vertex_co_get(ss, i)); + SCULPT_vertex_normal_get(ss, vertex, normal); + sub_v3_v3v3( + dir, + SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)), + SCULPT_vertex_co_get(ss, vertex)); cross_v3_v3v3( - boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex], dir, normal); - normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); - copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex], - SCULPT_vertex_co_get(ss, i)); + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i], dir, normal); + normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); + copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i], + SCULPT_vertex_co_get(ss, vertex)); } for (int i = 0; i < totvert; i++) { - if (boundary->edit_info[i].num_propagation_steps == BOUNDARY_STEPS_NONE) { + if (boundary->edit_info[i].propagation_steps_num == BOUNDARY_STEPS_NONE) { continue; } copy_v3_v3(boundary->bend.pivot_positions[i], - boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex]); + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]); copy_v3_v3(boundary->bend.pivot_rotation_axis[i], - boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); } } @@ -569,36 +602,37 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b boundary->slide.directions = MEM_calloc_arrayN(totvert, sizeof(float[3]), "slide directions"); for (int i = 0; i < totvert; i++) { - if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { + if (boundary->edit_info[i].propagation_steps_num != boundary->max_propagation_steps) { continue; } - sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex], - SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), - SCULPT_vertex_co_get(ss, i)); - normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex]); + sub_v3_v3v3( + boundary->slide.directions[boundary->edit_info[i].original_vertex_i], + SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)), + SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, i))); + normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i]); } for (int i = 0; i < totvert; i++) { - if (boundary->edit_info[i].num_propagation_steps == BOUNDARY_STEPS_NONE) { + if (boundary->edit_info[i].propagation_steps_num == BOUNDARY_STEPS_NONE) { continue; } copy_v3_v3(boundary->slide.directions[i], - boundary->slide.directions[boundary->edit_info[i].original_vertex]); + boundary->slide.directions[boundary->edit_info[i].original_vertex_i]); } } static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *boundary) { zero_v3(boundary->twist.pivot_position); - float(*poly_verts)[3] = MEM_malloc_arrayN( - boundary->num_vertices, sizeof(float[3]), "poly verts"); - for (int i = 0; i < boundary->num_vertices; i++) { - add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i])); - copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i])); + float(*poly_verts)[3] = MEM_malloc_arrayN(boundary->verts_num, sizeof(float[3]), "poly verts"); + for (int i = 0; i < boundary->verts_num; i++) { + add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->verts[i])); + copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->verts[i])); } - mul_v3_fl(boundary->twist.pivot_position, 1.0f / boundary->num_vertices); + mul_v3_fl(boundary->twist.pivot_position, 1.0f / boundary->verts_num); if (boundary->forms_loop) { - normal_poly_v3(boundary->twist.rotation_axis, poly_verts, boundary->num_vertices); + normal_poly_v3(boundary->twist.rotation_axis, poly_verts, boundary->verts_num); } else { sub_v3_v3v3(boundary->twist.rotation_axis, @@ -638,7 +672,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); float angle_factor = disp / ss->cache->radius; @@ -649,7 +683,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, const float angle = angle_factor * M_PI; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -660,7 +694,7 @@ 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.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); 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]); @@ -671,7 +705,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, add_v3_v3(target_co, boundary->bend.pivot_positions[vd.index]); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -692,12 +726,12 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + 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); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -708,7 +742,7 @@ 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.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -717,7 +751,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, strength); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -738,12 +772,12 @@ 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_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); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -754,7 +788,7 @@ 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.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -763,7 +797,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, strength); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -784,10 +818,10 @@ 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_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -798,7 +832,7 @@ 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.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -806,7 +840,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, boundary->edit_info[vd.index].strength_factor * mask * automask * strength); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -827,7 +861,7 @@ 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_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); float angle_factor = disp / ss->cache->radius; @@ -838,7 +872,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, const float angle = angle_factor * M_PI; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -849,7 +883,7 @@ 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.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); 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); @@ -860,7 +894,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, add_v3_v3(target_co, boundary->twist.pivot_position); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -881,10 +915,10 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (boundary->edit_info[vd.index].num_propagation_steps == -1) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } @@ -896,11 +930,11 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, float coord_accum[3] = {0.0f, 0.0f, 0.0f}; int total_neighbors = 0; - const int current_propagation_steps = boundary->edit_info[vd.index].num_propagation_steps; + const int current_propagation_steps = boundary->edit_info[vd.index].propagation_steps_num; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { - if (current_propagation_steps == boundary->edit_info[ni.index].num_propagation_steps) { - add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { + if (current_propagation_steps == boundary->edit_info[ni.index].propagation_steps_num) { + add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.vertex)); total_neighbors++; } } @@ -919,7 +953,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, target_co, vd.co, disp, boundary->edit_info[vd.index].strength_factor * mask * strength); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -933,7 +967,8 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn const int symm_area = ss->cache->mirror_symmetry_pass; if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - int initial_vertex; + PBVHVertRef initial_vertex; + if (ss->cache->mirror_symmetry_pass == 0) { initial_vertex = SCULPT_active_vertex_get(ss); } @@ -1017,8 +1052,8 @@ void SCULPT_boundary_edges_preview_draw(const uint gpuattr, } immUniformColor3fvAlpha(outline_col, outline_alpha); GPU_line_width(2.0f); - immBegin(GPU_PRIM_LINES, ss->boundary_preview->num_edges * 2); - for (int i = 0; i < ss->boundary_preview->num_edges; i++) { + immBegin(GPU_PRIM_LINES, ss->boundary_preview->edges_num * 2); + for (int i = 0; i < ss->boundary_preview->edges_num; i++) { immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->edges[i].v1)); immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->edges[i].v2)); } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.c b/source/blender/editors/sculpt_paint/sculpt_brush_types.c index 39544a41a87..245cbe0f54e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.c +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.c @@ -314,13 +314,13 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -412,13 +412,13 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -510,13 +510,13 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -628,13 +628,13 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -783,13 +783,13 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } } @@ -940,13 +940,13 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1066,13 +1066,13 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1219,7 +1219,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); } @@ -1267,12 +1267,12 @@ 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.index)); + mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); copy_v3_v3(proxy[vd.i], disp); } if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1330,7 +1330,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, float(*proxy)[3]; const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -1352,13 +1352,13 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1403,7 +1403,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, float(*proxy)[3]; const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -1426,7 +1426,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); sub_v3_v3v3(vec, orig_data.co, ss->cache->location); @@ -1436,7 +1436,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3(proxy[vd.i], orig_data.co); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1477,7 +1477,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( @@ -1497,7 +1497,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); const int vi = vd.index; @@ -1533,9 +1533,10 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, float normal[3]; if (use_persistent_base) { - SCULPT_vertex_persistent_normal_get(ss, vi, normal); + SCULPT_vertex_persistent_normal_get(ss, vd.vertex, normal); mul_v3_fl(normal, brush->height); - madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor); + madd_v3_v3v3fl( + final_co, SCULPT_vertex_persistent_co_get(ss, vd.vertex), normal, *disp_factor); } else { copy_v3_v3(normal, orig_data.no); @@ -1551,7 +1552,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, final_co); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1609,7 +1610,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float val[3]; @@ -1624,7 +1625,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1677,13 +1678,13 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1756,7 +1757,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float val1[3]; float val2[3]; @@ -1777,7 +1778,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, add_v3_v3v3(proxy[vd.i], val1, val2); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1872,7 +1873,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float disp_center[3]; float x_disp[3]; @@ -1896,7 +1897,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(proxy[vd.i], disp_center, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1964,7 +1965,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, float(*proxy)[3]; const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -1988,7 +1989,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); if (grab_silhouette) { @@ -2005,7 +2006,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(proxy[vd.i], grab_delta, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2052,7 +2053,7 @@ 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_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -2108,12 +2109,12 @@ 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.index)); + mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); copy_v3_v3(proxy[vd.i], final_disp); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2163,7 +2164,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; float(*proxy)[3]; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -2185,13 +2186,13 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2247,7 +2248,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; float(*proxy)[3]; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -2268,7 +2269,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float current_disp[3]; float current_disp_norm[3]; @@ -2290,10 +2291,10 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vertex_disp[3]; float vertex_disp_norm[3]; - sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); + sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co); normalize_v3_v3(vertex_disp_norm, vertex_disp); if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) { madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp)); @@ -2304,7 +2305,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(proxy[vd.i], final_disp, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2323,31 +2324,31 @@ void SCULPT_relax_vertex(SculptSession *ss, int neighbor_count = 0; zero_v3(smooth_pos); zero_v3(boundary_normal); - const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index); + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) { neighbor_count++; if (!filter_boundary_face_sets || - (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) { + (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.vertex))) { /* When the vertex to relax is boundary, use only connected boundary vertices for the average * position. */ if (is_boundary) { - if (!SCULPT_vertex_is_boundary(ss, ni.index)) { + if (!SCULPT_vertex_is_boundary(ss, ni.vertex)) { continue; } - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); avg_count++; /* Calculate a normal for the constraint plane using the edges of the boundary. */ float to_neighbor[3]; - sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co); + sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.vertex), vd->co); normalize_v3(to_neighbor); add_v3_v3(boundary_normal, to_neighbor); } else { - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); avg_count++; } } @@ -2376,7 +2377,7 @@ void SCULPT_relax_vertex(SculptSession *ss, normalize_v3_v3(vno, boundary_normal); } else { - SCULPT_vertex_normal_get(ss, vd->index, vno); + SCULPT_vertex_normal_get(ss, vd->vertex, vno); } if (is_zero_v3(vno)) { @@ -2404,7 +2405,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n]); @@ -2425,12 +2426,12 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2501,17 +2502,17 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float limit_co[3]; float disp[3]; - SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co); + SCULPT_vertex_limit_surface_get(ss, vd.vertex, limit_co); sub_v3_v3v3(disp, limit_co, vd.co); mul_v3_v3fl(proxy[vd.i], disp, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2567,7 +2568,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float current_disp[3]; @@ -2594,11 +2595,11 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, float weights_accum = 1.0f; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vertex_disp[3]; float vertex_disp_norm[3]; float neighbor_limit_co[3]; - SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co); + SCULPT_vertex_limit_surface_get(ss, ni.vertex, neighbor_limit_co); sub_v3_v3v3(vertex_disp, ss->cache->limit_surface_co[ni.index], ss->cache->limit_surface_co[vd.index]); @@ -2623,7 +2624,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, interp_v3_v3v3(vd.co, vd.co, new_co, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2638,7 +2639,7 @@ static void do_displacement_smear_store_prev_disp_task_cb_ex( PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { sub_v3_v3v3(ss->cache->prev_displacement[vd.index], - SCULPT_vertex_co_get(ss, vd.index), + SCULPT_vertex_co_get(ss, vd.vertex), ss->cache->limit_surface_co[vd.index]); } BKE_pbvh_vertex_iter_end; @@ -2657,9 +2658,11 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes totvert, sizeof(float[3]), "prev displacement"); ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_limit_surface_get(ss, vertex, ss->cache->limit_surface_co[i]); sub_v3_v3v3(ss->cache->prev_displacement[i], - SCULPT_vertex_co_get(ss, i), + SCULPT_vertex_co_get(ss, vertex), ss->cache->limit_surface_co[i]); } } @@ -2722,7 +2725,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, const float fade = bstrength * SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) * + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.vertex, thread_id) * ss->cache->pressure; float avg[3], val[3]; @@ -2736,7 +2739,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, val); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2799,7 +2802,7 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, } const float fade = SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id); + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.vertex, thread_id); if (bstrength > 0.0f) { (*vd.mask) += fade * bstrength * (1.0f - *vd.mask); @@ -2808,12 +2811,8 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, (*vd.mask) += fade * bstrength * (*vd.mask); } *vd.mask = clamp_f(*vd.mask, 0.0f, 1.0f); - - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } - BKE_pbvh_vertex_iter_end; } + BKE_pbvh_vertex_iter_end; } void SCULPT_do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 9d231f2ccd2..691dfa21851 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -220,13 +220,16 @@ static void cloth_brush_add_length_constraint(SculptSession *ss, length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL; + PBVHVertRef vertex1 = BKE_pbvh_index_to_vertex(ss->pbvh, v1); + PBVHVertRef vertex2 = BKE_pbvh_index_to_vertex(ss->pbvh, v2); + if (use_persistent) { - length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1), - SCULPT_vertex_persistent_co_get(ss, v2)); + length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, vertex1), + SCULPT_vertex_persistent_co_get(ss, vertex2)); } else { - length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1), - SCULPT_vertex_co_get(ss, v2)); + length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, vertex1), + SCULPT_vertex_co_get(ss, vertex2)); } length_constraint->strength = 1.0f; @@ -370,7 +373,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex( int tot_indices = 0; build_indices[tot_indices] = vd.index; tot_indices++; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { build_indices[tot_indices] = ni.index; tot_indices++; } @@ -540,7 +543,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float brush_disp[3]; @@ -611,13 +614,17 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } -static ListBase *cloth_brush_collider_cache_create(Depsgraph *depsgraph) +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) { + if (STREQ(object->id.name, ob->id.name)) { + continue; + } + CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type( ob, eModifierType_Collision); if (!cmd) { @@ -780,7 +787,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.index); + SCULPT_automasking_factor_get(automasking, ss, vd.vertex); madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v); madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v); @@ -798,7 +805,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( copy_v3_v3(vd.co, cloth_sim->pos[vd.index]); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -848,10 +855,13 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f); - const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) * - SCULPT_automasking_factor_get(automasking, ss, v1); - const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) * - SCULPT_automasking_factor_get(automasking, ss, v2); + PBVHVertRef vertex1 = BKE_pbvh_index_to_vertex(ss->pbvh, v1); + PBVHVertRef vertex2 = BKE_pbvh_index_to_vertex(ss->pbvh, v2); + + const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, vertex1)) * + SCULPT_automasking_factor_get(automasking, ss, vertex1); + const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, vertex2)) * + SCULPT_automasking_factor_get(automasking, ss, vertex2); float sim_location[3]; cloth_brush_simulation_location_get(ss, brush, sim_location); @@ -1029,13 +1039,14 @@ static void cloth_sim_initialize_default_node_state(SculptSession *ss, MEM_SAFE_FREE(nodes); } -SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss, +SculptClothSimulation *SCULPT_cloth_brush_simulation_create(Object *ob, const float cloth_mass, const float cloth_damping, const float cloth_softbody_strength, const bool use_collisions, const bool needs_deform_coords) { + SculptSession *ss = ob->sculpt; const int totverts = SCULPT_vertex_count_get(ss); SculptClothSimulation *cloth_sim; @@ -1073,7 +1084,7 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss, cloth_sim->softbody_strength = cloth_softbody_strength; if (use_collisions) { - cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph); + cloth_sim->collider_list = cloth_brush_collider_cache_create(ob, ss->depsgraph); } cloth_sim_initialize_default_node_state(ss, cloth_sim); @@ -1124,15 +1135,17 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation const bool has_deformation_pos = cloth_sim->deformation_pos != NULL; const bool has_softbody_pos = cloth_sim->softbody_pos != NULL; for (int i = 0; i < totverts; i++) { - copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + 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)); + 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, i)); + copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, vertex)); cloth_sim->deformation_strength[i] = 1.0f; } if (has_softbody_pos) { - copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, i)); + copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, vertex)); } } } @@ -1141,7 +1154,9 @@ void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSim { const int totverts = SCULPT_vertex_count_get(ss); for (int i = 0; i < totverts; i++) { - copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex)); } } @@ -1184,7 +1199,7 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode /* The simulation structure only needs to be created on the first symmetry pass. */ if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) { ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create( - ss, + ob, brush->cloth_mass, brush->cloth_damping, brush->cloth_constraint_softbody_strength, @@ -1422,13 +1437,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { float fade = vd.mask ? *vd.mask : 0.0f; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); fade = 1.0f - fade; float force[3] = {0.0f, 0.0f, 0.0f}; float disp[3], temp[3], transform[3][3]; if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) { - if (!SCULPT_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) { + if (!SCULPT_vertex_has_face_set(ss, vd.vertex, ss->filter_cache->active_face_set)) { continue; } } @@ -1447,7 +1462,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, break; case CLOTH_FILTER_INFLATE: { float normal[3]; - SCULPT_vertex_normal_get(ss, vd.index, normal); + SCULPT_vertex_normal_get(ss, vd.vertex, normal); mul_v3_v3fl(force, normal, fade * data->filter_strength); } break; case CLOTH_FILTER_EXPAND: @@ -1512,7 +1527,9 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent const int totverts = SCULPT_vertex_count_get(ss); for (int i = 0; i < totverts; i++) { - copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex)); } SculptThreadedTaskData data = { @@ -1562,7 +1579,7 @@ 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_undo_push_begin(ob, "Cloth filter"); + SCULPT_undo_push_begin(ob, op); SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob); @@ -1571,7 +1588,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping"); const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions"); ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create( - ss, + ob, cloth_mass, cloth_damping, 0.0f, diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index 00503087e39..8f87cd1b6ed 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -76,7 +76,7 @@ static bool sculpt_and_dynamic_topology_poll(bContext *C) /** \name Detail Flood Fill * \{ */ -static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) +static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Object *ob = CTX_data_active_object(C); @@ -106,7 +106,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); - SCULPT_undo_push_begin(ob, "Dynamic topology flood fill"); + SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); while (BKE_pbvh_bmesh_update_topology( @@ -174,13 +174,13 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2]) BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); /* Average the edge length of the connected edges to the active vertex. */ - int active_vertex = SCULPT_active_vertex_get(ss); + PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); const float *active_vertex_co = SCULPT_active_vertex_co_get(ss); float edge_length = 0.0f; int tot = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) { - edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.index)); + edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.vertex)); tot += 1; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -578,14 +578,14 @@ static void dyntopo_detail_size_sample_from_surface(Object *ob, DyntopoDetailSizeEditCustomData *cd) { SculptSession *ss = ob->sculpt; - const int active_vertex = SCULPT_active_vertex_get(ss); + const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); float len_accum = 0; int num_neighbors = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) { len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex), - SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_vertex_co_get(ss, ni.vertex)); num_neighbors++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 4f884420401..ad8a1cde9dc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -92,13 +92,16 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss) { int cd_node_layer_index; - char layer_id[] = "_dyntopo_node_id"; + 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); - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT32, layer_id); if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT32, layer_id); + 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, layer_id); + &ss->bm->vdata, CD_PROP_INT32, node_vertex_id); } ss->cd_vert_node_offset = CustomData_get_n_offset( @@ -108,11 +111,12 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss) 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, layer_id); + 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, layer_id); + 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, layer_id); + &ss->bm->pdata, CD_PROP_INT32, node_face_id); } ss->cd_face_node_offset = CustomData_get_n_offset( @@ -206,15 +210,13 @@ static void SCULPT_dynamic_topology_disable_ex( &geometry->ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, geometry->totloop); CustomData_copy( &geometry->pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly); - - BKE_mesh_update_customdata_pointers(me, false); } else { BKE_sculptsession_bm_to_me(ob, true); /* Reset Face Sets as they are no longer valid. */ if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) { - CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, me->totpoly); + CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_SET_DEFAULT, NULL, me->totpoly); } ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); for (int i = 0; i < me->totpoly; i++) { @@ -223,8 +225,9 @@ static void SCULPT_dynamic_topology_disable_ex( me->face_sets_color_default = 1; /* Sync the visibility to vertices manually as the pmap is still not initialized. */ - for (int i = 0; i < me->totvert; i++) { - me->mvert[i].flag &= ~ME_HIDE; + bool *hide_vert = (bool *)CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"); + if (hide_vert != NULL) { + memset(hide_vert, 0, sizeof(bool) * me->totvert); } } @@ -269,7 +272,7 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain, /* May be false in background mode. */ const bool use_undo = G.background ? (ED_undo_stack_get() != NULL) : true; if (use_undo) { - SCULPT_undo_push_begin(ob, "Dynamic topology disable"); + SCULPT_undo_push_begin_ex(ob, "Dynamic topology disable"); SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_END); } SCULPT_dynamic_topology_disable_ex(bmain, depsgraph, scene, ob, NULL); @@ -289,7 +292,7 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain, /* May be false in background mode. */ const bool use_undo = G.background ? (ED_undo_stack_get() != NULL) : true; if (use_undo) { - SCULPT_undo_push_begin(ob, "Dynamic topology enable"); + SCULPT_undo_push_begin_ex(ob, "Dynamic topology enable"); } SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); if (use_undo) { diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index fbf988d63e8..4aafeacfbff 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -140,10 +140,12 @@ enum { */ static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss, ExpandCache *expand_cache, - const int v) + const PBVHVertRef v) { + int v_i = BKE_pbvh_vertex_to_index(ss->pbvh, v); + for (int i = 0; i < EXPAND_SYMM_AREAS; i++) { - if (ss->vertex_info.connected_component[v] == expand_cache->active_connected_components[i]) { + if (ss->vertex_info.connected_component[v_i] == expand_cache->active_connected_components[i]) { return true; } } @@ -158,7 +160,7 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, const int f) { const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart]; - return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v); + return sculpt_expand_is_vert_in_active_component(ss, expand_cache, BKE_pbvh_make_vref(loop->v)); } /** @@ -167,14 +169,16 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, */ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, ExpandCache *expand_cache, - const int v) + const PBVHVertRef v) { + int v_i = BKE_pbvh_vertex_to_index(ss->pbvh, v); + if (expand_cache->texture_distortion_strength == 0.0f) { - return expand_cache->vert_falloff[v]; + return expand_cache->vert_falloff[v_i]; } if (!expand_cache->brush->mtex.tex) { - return expand_cache->vert_falloff[v]; + return expand_cache->vert_falloff[v_i]; } float rgba[4]; @@ -184,7 +188,7 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength * expand_cache->max_vert_falloff; - return expand_cache->vert_falloff[v] + distortion; + return expand_cache->vert_falloff[v_i] + distortion; } /** @@ -209,7 +213,9 @@ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache) * Main function to get the state of a vertex for the current state and settings of a #ExpandCache. * Returns true when the target data should be modified by expand. */ -static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v) +static bool sculpt_expand_state_get(SculptSession *ss, + ExpandCache *expand_cache, + const PBVHVertRef v) { if (!SCULPT_vertex_visible_get(ss, v)) { return false; @@ -303,7 +309,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_ */ static float sculpt_expand_gradient_value_get(SculptSession *ss, ExpandCache *expand_cache, - const int v) + const PBVHVertRef v) { if (!expand_cache->falloff_gradient) { return 1.0f; @@ -345,12 +351,13 @@ static float sculpt_expand_gradient_value_get(SculptSession *ss, static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCache *expand_cache) { const int totvert = SCULPT_vertex_count_get(ss); - BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); + BLI_bitmap *enabled_verts = BLI_BITMAP_NEW(totvert, "enabled verts"); for (int i = 0; i < totvert; i++) { - const bool enabled = sculpt_expand_state_get(ss, expand_cache, i); - BLI_BITMAP_SET(enabled_vertices, i, enabled); + const bool enabled = sculpt_expand_state_get( + ss, expand_cache, BKE_pbvh_index_to_vertex(ss->pbvh, i)); + BLI_BITMAP_SET(enabled_verts, i, enabled); } - return enabled_vertices; + return enabled_verts; } /** @@ -359,33 +366,35 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa * vertex that is not enabled. */ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, - const BLI_bitmap *enabled_vertices, + const BLI_bitmap *enabled_verts, const bool use_mesh_boundary) { const int totvert = SCULPT_vertex_count_get(ss); - BLI_bitmap *boundary_vertices = BLI_BITMAP_NEW(totvert, "boundary vertices"); + BLI_bitmap *boundary_verts = BLI_BITMAP_NEW(totvert, "boundary verts"); for (int i = 0; i < totvert; i++) { - if (!BLI_BITMAP_TEST(enabled_vertices, i)) { + if (!BLI_BITMAP_TEST(enabled_verts, i)) { continue; } + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + bool is_expand_boundary = false; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { - if (!BLI_BITMAP_TEST(enabled_vertices, ni.index)) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + if (!BLI_BITMAP_TEST(enabled_verts, ni.index)) { is_expand_boundary = true; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, i)) { + if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, vertex)) { is_expand_boundary = true; } - BLI_BITMAP_SET(boundary_vertices, i, is_expand_boundary); + BLI_BITMAP_SET(boundary_verts, i, is_expand_boundary); } - return boundary_vertices; + return boundary_verts; } /* Functions implementing different algorithms for initializing falloff values. */ @@ -394,12 +403,12 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, * Utility function to get the closet vertex after flipping an original vertex position based on * an symmetry pass iteration index. */ -static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, - const char symm_it, - const int original_vertex) +static PBVHVertRef sculpt_expand_get_vertex_index_for_symmetry_pass( + Object *ob, const char symm_it, const PBVHVertRef original_vertex) { SculptSession *ss = ob->sculpt; - int symm_vertex = SCULPT_EXPAND_VERTEX_NONE; + PBVHVertRef symm_vertex = {SCULPT_EXPAND_VERTEX_NONE}; + if (symm_it == 0) { symm_vertex = original_vertex; } @@ -415,7 +424,7 @@ static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, * Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking * symmetry into account. */ -static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v) +static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const PBVHVertRef v) { return SCULPT_geodesic_from_vertex_and_symm(sd, ob, v, FLT_MAX); } @@ -432,20 +441,23 @@ typedef struct ExpandFloodFillData { } ExpandFloodFillData; static bool expand_topology_floodfill_cb( - SculptSession *UNUSED(ss), int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *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); + ExpandFloodFillData *data = userdata; if (!is_duplicate) { - const float to_it = data->dists[from_v] + 1.0f; - data->dists[to_v] = to_it; + const float to_it = data->dists[from_v_i] + 1.0f; + data->dists[to_v_i] = to_it; } else { - data->dists[to_v] = data->dists[from_v]; + data->dists[to_v_i] = data->dists[from_v_i]; } return true; } -static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int v) +static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const PBVHVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -470,23 +482,26 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons * This creates falloff patterns that follow and snap to the hard edges of the object. */ static bool mask_expand_normal_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *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); + ExpandFloodFillData *data = userdata; if (!is_duplicate) { float current_normal[3], prev_normal[3]; SCULPT_vertex_normal_get(ss, to_v, current_normal); SCULPT_vertex_normal_get(ss, from_v, prev_normal); - const float from_edge_factor = data->edge_factor[from_v]; - data->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * from_edge_factor; - data->dists[to_v] = dot_v3v3(data->original_normal, current_normal) * - powf(from_edge_factor, data->edge_sensitivity); - CLAMP(data->dists[to_v], 0.0f, 1.0f); + const float from_edge_factor = data->edge_factor[from_v_i]; + data->edge_factor[to_v_i] = dot_v3v3(current_normal, prev_normal) * from_edge_factor; + data->dists[to_v_i] = dot_v3v3(data->original_normal, current_normal) * + powf(from_edge_factor, data->edge_sensitivity); + CLAMP(data->dists[to_v_i], 0.0f, 1.0f); } else { /* PBVH_GRIDS duplicate handling. */ - data->edge_factor[to_v] = data->edge_factor[from_v]; - data->dists[to_v] = data->dists[from_v]; + data->edge_factor[to_v_i] = data->edge_factor[from_v_i]; + data->dists[to_v_i] = data->dists[from_v_i]; } return true; @@ -494,7 +509,7 @@ static bool mask_expand_normal_floodfill_cb( static float *sculpt_expand_normal_falloff_create(Sculpt *sd, Object *ob, - const int v, + const PBVHVertRef v, const float edge_sensitivity) { SculptSession *ss = ob->sculpt; @@ -520,9 +535,11 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, for (int repeat = 0; repeat < 2; repeat++) { for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg = 0.0f; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { avg += dists[ni.index]; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -539,7 +556,7 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, * Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into * account. */ -static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) +static float *sculpt_expand_spherical_falloff_create(Object *ob, const PBVHVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -554,11 +571,14 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); - if (symm_vertex != -1) { + const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, v); + if (symm_vertex.i != SCULPT_EXPAND_VERTEX_NONE) { const float *co = SCULPT_vertex_co_get(ss, symm_vertex); for (int i = 0; i < totvert; i++) { - dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, i))); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, vertex))); } } } @@ -571,13 +591,13 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) * boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it * stays parallel to the boundary, increasing the falloff value by 1 on each step. */ -static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const int v) +static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const PBVHVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "spherical dist"); - BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); - GSQueue *queue = BLI_gsqueue_new(sizeof(int)); + BLI_bitmap *visited_verts = BLI_BITMAP_NEW(totvert, "visited verts"); + GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef)); /* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */ const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -586,16 +606,17 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); + const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, v); SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, symm_vertex, FLT_MAX); if (!boundary) { continue; } - for (int i = 0; i < boundary->num_vertices; i++) { - BLI_gsqueue_push(queue, &boundary->vertices[i]); - BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices[i]); + 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]); } SCULPT_boundary_data_free(boundary); } @@ -607,23 +628,25 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i /* Propagate the values from the boundaries to the rest of the mesh. */ while (!BLI_gsqueue_is_empty(queue)) { - int v_next; + PBVHVertRef v_next; + BLI_gsqueue_pop(queue, &v_next); + int v_next_i = BKE_pbvh_vertex_to_index(ss->pbvh, v_next); SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_next, ni) { - if (BLI_BITMAP_TEST(visited_vertices, ni.index)) { + if (BLI_BITMAP_TEST(visited_verts, ni.index)) { continue; } - dists[ni.index] = dists[v_next] + 1.0f; - BLI_BITMAP_ENABLE(visited_vertices, ni.index); - BLI_gsqueue_push(queue, &ni.index); + dists[ni.index] = dists[v_next_i] + 1.0f; + BLI_BITMAP_ENABLE(visited_verts, ni.index); + BLI_gsqueue_push(queue, &ni.vertex); } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); } BLI_gsqueue_free(queue); - MEM_freeN(visited_vertices); + MEM_freeN(visited_verts); return dists; } @@ -632,7 +655,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i * the base mesh faces when checking a vertex neighbor. For this reason, this is not implement * using the general flood-fill and sculpt neighbors accessors. */ -static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) +static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -646,18 +669,20 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) } /* Search and mask as visited the initial vertices using the enabled symmetry passes. */ - BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); - GSQueue *queue = BLI_gsqueue_new(sizeof(int)); + BLI_bitmap *visited_verts = BLI_BITMAP_NEW(totvert, "visited verts"); + GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef)); const char symm = SCULPT_mesh_symmetry_xyz_get(ob); for (char symm_it = 0; symm_it <= symm; symm_it++) { if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); + const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, v); + int symm_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, symm_vertex); BLI_gsqueue_push(queue, &symm_vertex); - BLI_BITMAP_ENABLE(visited_vertices, symm_vertex); + BLI_BITMAP_ENABLE(visited_verts, symm_vertex_i); } if (BLI_gsqueue_is_empty(queue)) { @@ -665,26 +690,28 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) } /* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */ - Mesh *mesh = ob->data; while (!BLI_gsqueue_is_empty(queue)) { - int v_next; + PBVHVertRef v_next; BLI_gsqueue_pop(queue, &v_next); - for (int j = 0; j < ss->pmap[v_next].count; j++) { - MPoly *p = &ss->mpoly[ss->pmap[v_next].indices[j]]; + + int v_next_i = BKE_pbvh_vertex_to_index(ss->pbvh, v_next); + + for (int j = 0; j < ss->pmap[v_next_i].count; j++) { + const MPoly *p = &ss->mpoly[ss->pmap[v_next_i].indices[j]]; for (int l = 0; l < p->totloop; l++) { - const int neighbor_v = mesh->mloop[p->loopstart + l].v; - if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) { + const PBVHVertRef neighbor_v = BKE_pbvh_make_vref(ss->mloop[p->loopstart + l].v); + if (BLI_BITMAP_TEST(visited_verts, neighbor_v.i)) { continue; } - dists[neighbor_v] = dists[v_next] + 1.0f; - BLI_BITMAP_ENABLE(visited_vertices, neighbor_v); + dists[neighbor_v.i] = dists[v_next_i] + 1.0f; + BLI_BITMAP_ENABLE(visited_verts, neighbor_v.i); BLI_gsqueue_push(queue, &neighbor_v); } } } BLI_gsqueue_free(queue); - MEM_freeN(visited_vertices); + MEM_freeN(visited_verts); return dists; } @@ -701,11 +728,13 @@ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss, const int totvert = SCULPT_vertex_count_get(ss); expand_cache->max_vert_falloff = -FLT_MAX; for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (expand_cache->vert_falloff[i] == FLT_MAX) { continue; } - if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, vertex)) { continue; } @@ -747,11 +776,11 @@ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss, Mesh *mesh, ExpandCache *expand_cache) { - + const MPoly *polys = BKE_mesh_polys(mesh); const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); for (int p = 0; p < mesh->totpoly; p++) { - MPoly *poly = &mesh->mpoly[p]; + const MPoly *poly = &polys[p]; float accum = 0.0f; for (int l = 0; l < poly->totloop; l++) { const int grid_loop_index = (poly->loopstart + l) * key->grid_area; @@ -765,11 +794,14 @@ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss, static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expand_cache) { + const MPoly *polys = BKE_mesh_polys(mesh); + const MLoop *loops = BKE_mesh_loops(mesh); + for (int p = 0; p < mesh->totpoly; p++) { - MPoly *poly = &mesh->mpoly[p]; + const MPoly *poly = &polys[p]; float accum = 0.0f; for (int l = 0; l < poly->totloop; l++) { - MLoop *loop = &mesh->mloop[l + poly->loopstart]; + const MLoop *loop = &loops[l + poly->loopstart]; accum += expand_cache->vert_falloff[loop->v]; } expand_cache->face_falloff[p] = accum / poly->totloop; @@ -810,27 +842,27 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s */ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, ExpandCache *expand_cache, - BLI_bitmap *enabled_vertices) + BLI_bitmap *enabled_verts) { SculptSession *ss = ob->sculpt; BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES); - GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); - BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false); + GSet *initial_verts = BLI_gset_int_new("initial_verts"); + BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled(ss, enabled_verts, false); const int totvert = SCULPT_vertex_count_get(ss); for (int i = 0; i < totvert; i++) { - if (!BLI_BITMAP_TEST(boundary_vertices, i)) { + if (!BLI_BITMAP_TEST(boundary_verts, i)) { continue; } - BLI_gset_add(initial_vertices, POINTER_FROM_INT(i)); + BLI_gset_add(initial_verts, POINTER_FROM_INT(i)); } - MEM_freeN(boundary_vertices); + MEM_freeN(boundary_verts); MEM_SAFE_FREE(expand_cache->vert_falloff); MEM_SAFE_FREE(expand_cache->face_falloff); - expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_vertices, FLT_MAX); - BLI_gset_free(initial_vertices, NULL); + expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_verts, FLT_MAX); + BLI_gset_free(initial_verts, NULL); } /** @@ -839,7 +871,7 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, */ static void sculpt_expand_topology_from_state_boundary(Object *ob, ExpandCache *expand_cache, - BLI_bitmap *enabled_vertices) + BLI_bitmap *enabled_verts) { MEM_SAFE_FREE(expand_cache->vert_falloff); MEM_SAFE_FREE(expand_cache->face_falloff); @@ -848,17 +880,19 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob, const int totvert = SCULPT_vertex_count_get(ss); float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "topology dist"); - BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false); + BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled(ss, enabled_verts, false); SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); for (int i = 0; i < totvert; i++) { - if (!BLI_BITMAP_TEST(boundary_vertices, i)) { + if (!BLI_BITMAP_TEST(boundary_verts, i)) { continue; } - SCULPT_floodfill_add_and_skip_initial(&flood, i); + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + SCULPT_floodfill_add_and_skip_initial(&flood, vertex); } - MEM_freeN(boundary_vertices); + MEM_freeN(boundary_verts); ExpandFloodFillData fdata; fdata.dists = dists; @@ -880,7 +914,7 @@ static void sculpt_expand_resursion_step_add(Object *ob, return; } - BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); + BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache); /* Each time a new recursion step is created, reset the distortion strength. This is the expected * result from the recursion, as otherwise the new falloff will render with undesired distortion @@ -889,10 +923,10 @@ static void sculpt_expand_resursion_step_add(Object *ob, switch (recursion_type) { case SCULPT_EXPAND_RECURSION_GEODESICS: - sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices); + sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_verts); break; case SCULPT_EXPAND_RECURSION_TOPOLOGY: - sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_vertices); + sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_verts); break; } @@ -902,7 +936,7 @@ static void sculpt_expand_resursion_step_add(Object *ob, sculpt_expand_update_max_face_falloff_factor(ss, expand_cache); } - MEM_freeN(enabled_vertices); + MEM_freeN(enabled_verts); } /* Face Set Boundary falloff. */ @@ -919,30 +953,34 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); + BLI_bitmap *enabled_verts = BLI_BITMAP_NEW(totvert, "enabled verts"); for (int i = 0; i < totvert; i++) { - if (!SCULPT_vertex_has_unique_face_set(ss, i)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) { continue; } - if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { continue; } - BLI_BITMAP_ENABLE(enabled_vertices, i); + BLI_BITMAP_ENABLE(enabled_verts, i); } if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices); + sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_verts); } else { - sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_vertices); + sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_verts); } - MEM_freeN(enabled_vertices); + MEM_freeN(enabled_verts); if (internal_falloff) { for (int i = 0; i < totvert; i++) { - if (!(SCULPT_vertex_has_face_set(ss, i, active_face_set) && - SCULPT_vertex_has_unique_face_set(ss, i))) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!(SCULPT_vertex_has_face_set(ss, vertex, active_face_set) && + SCULPT_vertex_has_unique_face_set(ss, vertex))) { continue; } expand_cache->vert_falloff[i] *= -1.0f; @@ -960,7 +998,9 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, } else { for (int i = 0; i < totvert; i++) { - if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { continue; } expand_cache->vert_falloff[i] = 0.0f; @@ -976,7 +1016,7 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create( ExpandCache *expand_cache, Sculpt *sd, Object *ob, - const int v, + const PBVHVertRef v, eSculptExpandFalloffType falloff_type) { MEM_SAFE_FREE(expand_cache->vert_falloff); @@ -1046,7 +1086,7 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, expand_cache->snap = false; expand_cache->invert = false; - BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); + BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache); const int totface = ss->totfaces; for (int i = 0; i < totface; i++) { @@ -1055,11 +1095,11 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, } for (int p = 0; p < totface; p++) { - MPoly *poly = &ss->mpoly[p]; + const MPoly *poly = &ss->mpoly[p]; bool any_disabled = false; for (int l = 0; l < poly->totloop; l++) { - MLoop *loop = &ss->mloop[l + poly->loopstart]; - if (!BLI_BITMAP_TEST(enabled_vertices, loop->v)) { + const MLoop *loop = &ss->mloop[l + poly->loopstart]; + if (!BLI_BITMAP_TEST(enabled_verts, loop->v)) { any_disabled = true; break; } @@ -1070,7 +1110,7 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, } } - MEM_freeN(enabled_vertices); + MEM_freeN(enabled_verts); expand_cache->snap = prev_snap_state; expand_cache->invert = prev_invert_state; } @@ -1128,7 +1168,7 @@ static void sculpt_expand_restore_color_data(SculptSession *ss, ExpandCache *exp PBVHNode *node = nodes[n]; PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_vertex_color_set(ss, vd.index, expand_cache->original_colors[vd.index]); + SCULPT_vertex_color_set(ss, vd.vertex, expand_cache->original_colors[vd.index]); } BKE_pbvh_vertex_iter_end; BKE_pbvh_node_mark_redraw(node); @@ -1215,12 +1255,12 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { const float initial_mask = *vd.mask; - const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); + const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex); float new_mask; if (enabled) { - new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index); + new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex); } else { new_mask = 0.0f; @@ -1236,9 +1276,6 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, *vd.mask = clamp_f(new_mask, 0.0f, 1.0f); any_changed = true; - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } } BKE_pbvh_vertex_iter_end; if (any_changed) { @@ -1287,13 +1324,13 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { float initial_color[4]; - SCULPT_vertex_color_get(ss, vd.index, initial_color); + SCULPT_vertex_color_get(ss, vd.vertex, initial_color); - const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); + const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex); float fade; if (enabled) { - fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index); + fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex); } else { fade = 0.0f; @@ -1314,12 +1351,9 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, continue; } - SCULPT_vertex_color_set(ss, vd.index, final_color); + SCULPT_vertex_color_set(ss, vd.vertex, final_color); any_changed = true; - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } } BKE_pbvh_vertex_iter_end; if (any_changed) { @@ -1364,14 +1398,18 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c if (expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { expand_cache->original_mask = MEM_malloc_arrayN(totvert, sizeof(float), "initial mask"); for (int i = 0; i < totvert; i++) { - expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, i); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, vertex); } } if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) { expand_cache->original_colors = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_color_get(ss, i, expand_cache->original_colors[i]); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_color_get(ss, vertex, expand_cache->original_colors[i]); } } } @@ -1394,14 +1432,16 @@ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expa } } -static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int vertex) +static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const PBVHVertRef vertex) { SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; ExpandCache *expand_cache = ss->expand_cache; + int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + /* Update the active factor in the cache. */ - if (vertex == SCULPT_EXPAND_VERTEX_NONE) { + if (vertex.i == SCULPT_EXPAND_VERTEX_NONE) { /* This means that the cursor is not over the mesh, so a valid active falloff can't be * determined. In this situations, don't evaluate enabled states and default all vertices in * connected components to enabled. */ @@ -1409,7 +1449,7 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v expand_cache->all_enabled = true; } else { - expand_cache->active_falloff = expand_cache->vert_falloff[vertex]; + expand_cache->active_falloff = expand_cache->vert_falloff[vertex_i]; expand_cache->all_enabled = false; } @@ -1450,14 +1490,16 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v * Updates the #SculptSession cursor data and gets the active vertex * if the cursor is over the mesh. */ -static int sculpt_expand_target_vertex_update_and_get(bContext *C, Object *ob, const float mval[2]) +static PBVHVertRef sculpt_expand_target_vertex_update_and_get(bContext *C, + Object *ob, + const float mval[2]) { SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false)) { return SCULPT_active_vertex_get(ss); } - return SCULPT_EXPAND_VERTEX_NONE; + return BKE_pbvh_make_vref(SCULPT_EXPAND_VERTEX_NONE); } /** @@ -1472,7 +1514,7 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache const bool initial_invert_state = expand_cache->invert; expand_cache->invert = false; - BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); + BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache); /* For boundary topology, position the pivot using only the boundary of the enabled vertices, * without taking mesh boundary into account. This allows to create deformations like bending the @@ -1480,8 +1522,8 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache const float use_mesh_boundary = expand_cache->falloff_type != SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; - BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled( - ss, enabled_vertices, use_mesh_boundary); + BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled( + ss, enabled_verts, use_mesh_boundary); /* Ignore invert state, as this is the expected behavior in most cases and mask are created in * inverted state by default. */ @@ -1493,15 +1535,17 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache const float *expand_init_co = SCULPT_vertex_co_get(ss, expand_cache->initial_active_vertex); for (int i = 0; i < totvert; i++) { - if (!BLI_BITMAP_TEST(boundary_vertices, i)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!BLI_BITMAP_TEST(boundary_verts, i)) { continue; } - if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, vertex)) { continue; } - const float *vertex_co = SCULPT_vertex_co_get(ss, i); + const float *vertex_co = SCULPT_vertex_co_get(ss, vertex); if (!SCULPT_check_vertex_pivot_symmetry(vertex_co, expand_init_co, symm)) { continue; @@ -1511,8 +1555,8 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache total++; } - MEM_freeN(enabled_vertices); - MEM_freeN(boundary_vertices); + MEM_freeN(enabled_verts); + MEM_freeN(boundary_verts); if (total > 0) { mul_v3_v3fl(ss->pivot_pos, avg, 1.0f / total); @@ -1556,9 +1600,8 @@ static void sculpt_expand_finish(bContext *C) * Finds and stores in the #ExpandCache the sculpt connected component index for each symmetry pass * needed for expand. */ -static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, - ExpandCache *expand_cache, - const int initial_vertex) +static void sculpt_expand_find_active_connected_components_from_vert( + Object *ob, ExpandCache *expand_cache, const PBVHVertRef initial_vertex) { SculptSession *ss = ob->sculpt; for (int i = 0; i < EXPAND_SYMM_AREAS; i++) { @@ -1571,11 +1614,13 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( ob, symm_it, initial_vertex); + int symm_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, symm_vertex); + expand_cache->active_connected_components[(int)symm_it] = - ss->vertex_info.connected_component[symm_vertex]; + ss->vertex_info.connected_component[symm_vertex_i]; } } @@ -1589,14 +1634,20 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C, const float mval[2]) { SculptSession *ss = ob->sculpt; - int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval); - if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) { + + PBVHVertRef initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval); + + if (initial_vertex.i == SCULPT_EXPAND_VERTEX_NONE) { /* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active * vertex in the sculpt session. */ initial_vertex = SCULPT_active_vertex_get(ss); } + + int initial_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, initial_vertex); + copy_v2_v2(ss->expand_cache->initial_mouse, mval); expand_cache->initial_active_vertex = initial_vertex; + expand_cache->initial_active_vertex_i = initial_vertex_i; expand_cache->initial_active_face_set = SCULPT_active_face_set_get(ss); if (expand_cache->next_face_set == SCULPT_FACE_SET_NONE) { @@ -1696,7 +1747,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event /* Update and get the active vertex (and face) from the cursor. */ const float mval_fl[2] = {UNPACK2(event->mval)}; - const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval_fl); + const PBVHVertRef target_expand_vertex = sculpt_expand_target_vertex_update_and_get( + C, ob, mval_fl); /* Handle the modal keymap state changes. */ ExpandCache *expand_cache = ss->expand_cache; @@ -1864,10 +1916,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event /* Add new Face Sets IDs to the snapping gset if enabled. */ if (expand_cache->snap) { const int active_face_set_id = sculpt_expand_active_face_set_id_get(ss, expand_cache); - if (!BLI_gset_haskey(expand_cache->snap_enabled_face_sets, - POINTER_FROM_INT(active_face_set_id))) { - BLI_gset_add(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(active_face_set_id)); - } + /* The key may exist, in that case this does nothing. */ + BLI_gset_add(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(active_face_set_id)); } /* Update the sculpt data with the current state of the #ExpandCache. */ @@ -1890,6 +1940,8 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets, { const int totface = ss->totfaces; MeshElemMap *pmap = ss->pmap; + const MPoly *polys = BKE_mesh_polys(mesh); + const MLoop *loops = BKE_mesh_loops(mesh); /* Check that all the face sets IDs in the mesh are not equal to `delete_id` * before attempting to delete it. */ @@ -1924,9 +1976,9 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets, while (BLI_LINKSTACK_SIZE(queue)) { const int f_index = POINTER_AS_INT(BLI_LINKSTACK_POP(queue)); int other_id = delete_id; - const MPoly *c_poly = &mesh->mpoly[f_index]; + const MPoly *c_poly = &polys[f_index]; for (int l = 0; l < c_poly->totloop; l++) { - const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l]; + const MLoop *c_loop = &loops[c_poly->loopstart + l]; const MeshElemMap *vert_map = &pmap[c_loop->v]; for (int i = 0; i < vert_map->count; i++) { @@ -2076,7 +2128,7 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even sculpt_expand_ensure_sculptsession_data(ob); /* Initialize undo. */ - SCULPT_undo_push_begin(ob, "expand"); + SCULPT_undo_push_begin(ob, op); sculpt_expand_undo_push(ob, ss->expand_cache); /* Set the initial element for expand from the event position. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index ce704e619ea..64bc6188bbc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -142,7 +142,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) { @@ -161,11 +161,11 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); if (fade > 0.05f) { - SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set); + SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set); } } } @@ -199,7 +199,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } - if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) { + if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) { continue; } @@ -210,12 +210,12 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -317,15 +317,18 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - SCULPT_undo_push_begin(ob, "face set change"); + SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); const int next_face_set = SCULPT_face_set_next_available_get(ss); if (mode == SCULPT_FACE_SET_MASKED) { for (int i = 0; i < tot_vert; i++) { - if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) { - SCULPT_vertex_face_set_set(ss, i, next_face_set); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (SCULPT_vertex_mask_get(ss, vertex) >= threshold && + SCULPT_vertex_visible_get(ss, vertex)) { + SCULPT_vertex_face_set_set(ss, vertex, next_face_set); } } } @@ -337,7 +340,9 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) * sets and the performance hit of rendering the overlay. */ bool all_visible = true; for (int i = 0; i < tot_vert; i++) { - if (!SCULPT_vertex_visible_get(ss, i)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_visible_get(ss, vertex)) { all_visible = false; break; } @@ -351,15 +356,19 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } for (int i = 0; i < tot_vert; i++) { - if (SCULPT_vertex_visible_get(ss, i)) { - SCULPT_vertex_face_set_set(ss, i, next_face_set); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (SCULPT_vertex_visible_get(ss, vertex)) { + SCULPT_vertex_face_set_set(ss, vertex, next_face_set); } } } if (mode == SCULPT_FACE_SET_ALL) { for (int i = 0; i < tot_vert; i++) { - SCULPT_vertex_face_set_set(ss, i, next_face_set); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_face_set_set(ss, vertex, next_face_set); } } @@ -698,7 +707,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - SCULPT_undo_push_begin(ob, "face set change"); + SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); const float threshold = RNA_float_get(op->ptr, "threshold"); @@ -737,7 +746,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) 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_vertices(ob); + SCULPT_visibility_sync_all_face_sets_to_verts(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); @@ -847,7 +856,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) 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, "Hide area"); + SCULPT_undo_push_begin(ob, op); PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; @@ -869,7 +878,9 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) * 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++) { - if (!SCULPT_vertex_visible_get(ss, i)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_visible_get(ss, vertex)) { hidden_vertex = true; break; } @@ -922,7 +933,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) } /* Sync face sets visibility and vertex visibility. */ - SCULPT_visibility_sync_all_face_sets_to_vertices(ob); + SCULPT_visibility_sync_all_face_sets_to_verts(ob); SCULPT_undo_push_end(ob); @@ -1085,13 +1096,16 @@ static void sculpt_face_set_grow(Object *ob, const bool modify_hidden) { Mesh *mesh = BKE_mesh_from_object(ob); + const MPoly *polys = BKE_mesh_polys(mesh); + const MLoop *loops = BKE_mesh_loops(mesh); + for (int p = 0; p < mesh->totpoly; p++) { if (!modify_hidden && prev_face_sets[p] <= 0) { continue; } - const MPoly *c_poly = &mesh->mpoly[p]; + const MPoly *c_poly = &polys[p]; for (int l = 0; l < c_poly->totloop; l++) { - const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l]; + const MLoop *c_loop = &loops[c_poly->loopstart + l]; const MeshElemMap *vert_map = &ss->pmap[c_loop->v]; for (int i = 0; i < vert_map->count; i++) { const int neighbor_face_index = vert_map->indices[i]; @@ -1113,14 +1127,16 @@ static void sculpt_face_set_shrink(Object *ob, const bool modify_hidden) { Mesh *mesh = BKE_mesh_from_object(ob); + const MPoly *polys = BKE_mesh_polys(mesh); + const MLoop *loops = BKE_mesh_loops(mesh); for (int p = 0; p < mesh->totpoly; p++) { if (!modify_hidden && prev_face_sets[p] <= 0) { continue; } if (abs(prev_face_sets[p]) == active_face_set_id) { - const MPoly *c_poly = &mesh->mpoly[p]; + const MPoly *c_poly = &polys[p]; for (int l = 0; l < c_poly->totloop; l++) { - const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l]; + const MLoop *c_loop = &loops[c_poly->loopstart + l]; const MeshElemMap *vert_map = &ss->pmap[c_loop->v]; for (int i = 0; i < vert_map->count; i++) { const int neighbor_face_index = vert_map->indices[i]; @@ -1217,19 +1233,21 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob, const int totvert = SCULPT_vertex_count_get(ss); Mesh *mesh = ob->data; - bool *fair_vertices = MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"); + bool *fair_verts = MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"); SCULPT_boundary_info_ensure(ob); for (int i = 0; i < totvert; i++) { - fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) && - SCULPT_vertex_has_face_set(ss, i, active_face_set_id) && - SCULPT_vertex_has_unique_face_set(ss, i); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + fair_verts[i] = !SCULPT_vertex_is_boundary(ss, vertex) && + SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id) && + SCULPT_vertex_has_unique_face_set(ss, vertex); } MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); - BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order); - MEM_freeN(fair_vertices); + BKE_mesh_prefair_and_fair_verts(mesh, mvert, fair_verts, fair_order); + MEM_freeN(fair_verts); } static void sculpt_face_set_apply_edit(Object *ob, @@ -1304,9 +1322,10 @@ static void sculpt_face_set_edit_modify_geometry(bContext *C, Object *ob, const int active_face_set, const eSculptFaceSetEditMode mode, - const bool modify_hidden) + const bool modify_hidden, + wmOperator *op) { - ED_sculpt_undo_geometry_begin(ob, "edit face set delete geometry"); + 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); @@ -1320,7 +1339,7 @@ static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **node PBVH *pbvh = ss->pbvh; /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ - SCULPT_visibility_sync_all_face_sets_to_vertices(ob); + SCULPT_visibility_sync_all_face_sets_to_verts(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); @@ -1336,7 +1355,8 @@ static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **node static void sculpt_face_set_edit_modify_face_sets(Object *ob, const int active_face_set, const eSculptFaceSetEditMode mode, - const bool modify_hidden) + const bool modify_hidden, + wmOperator *op) { PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; @@ -1346,7 +1366,7 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, if (!nodes) { return; } - SCULPT_undo_push_begin(ob, "face set edit"); + SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); SCULPT_undo_push_end(ob); @@ -1357,7 +1377,8 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, static void sculpt_face_set_edit_modify_coordinates(bContext *C, Object *ob, const int active_face_set, - const eSculptFaceSetEditMode mode) + const eSculptFaceSetEditMode mode, + wmOperator *op) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; SculptSession *ss = ob->sculpt; @@ -1365,7 +1386,7 @@ static void sculpt_face_set_edit_modify_coordinates(bContext *C, PBVHNode **nodes; int totnode; BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); - SCULPT_undo_push_begin(ob, "face set edit"); + SCULPT_undo_push_begin(ob, op); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update(nodes[i]); SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS); @@ -1408,15 +1429,15 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven switch (mode) { case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY: - sculpt_face_set_edit_modify_geometry(C, ob, active_face_set, mode, modify_hidden); + sculpt_face_set_edit_modify_geometry(C, ob, active_face_set, mode, modify_hidden, op); break; case SCULPT_FACE_SET_EDIT_GROW: case SCULPT_FACE_SET_EDIT_SHRINK: - sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden); + sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden, op); break; case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS: case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY: - sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode); + sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode, op); break; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 95c01d24c6d..161fc563950 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -93,7 +93,7 @@ static void color_filter_task_cb(void *__restrict userdata, const int mode = data->filter_type; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -104,7 +104,7 @@ static void color_filter_task_cb(void *__restrict userdata, 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.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); if (fade == 0.0f) { continue; } @@ -189,10 +189,10 @@ static void color_filter_task_cb(void *__restrict userdata, case COLOR_FILTER_SMOOTH: { fade = clamp_f(fade, -1.0f, 1.0f); float smooth_color[4]; - SCULPT_neighbor_color_average(ss, smooth_color, vd.index); + SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); if (fade < 0.0f) { interp_v4_v4v4(smooth_color, smooth_color, col, 0.5f); @@ -224,11 +224,7 @@ static void color_filter_task_cb(void *__restrict userdata, } } - SCULPT_vertex_color_set(ss, vd.index, final_color); - - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } + SCULPT_vertex_color_set(ss, vd.vertex, final_color); } BKE_pbvh_vertex_iter_end; BKE_pbvh_node_mark_update_color(data->nodes[n]); @@ -244,7 +240,8 @@ static void sculpt_color_presmooth_init(SculptSession *ss) } for (int i = 0; i < totvert; i++) { - SCULPT_vertex_color_get(ss, i, ss->filter_cache->pre_smoothed_color[i]); + SCULPT_vertex_color_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, i), ss->filter_cache->pre_smoothed_color[i]); } for (int iteration = 0; iteration < 2; iteration++) { @@ -253,7 +250,7 @@ static void sculpt_color_presmooth_init(SculptSession *ss) int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, BKE_pbvh_index_to_vertex(ss->pbvh, i), ni) { float col[4] = {0}; copy_v4_v4(col, ss->filter_cache->pre_smoothed_color[ni.index]); @@ -349,7 +346,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - SCULPT_undo_push_begin(ob, "color filter"); + SCULPT_undo_push_begin(ob, op); BKE_sculpt_color_layer_create_if_needed(ob); /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index ea3f56d0859..cba1d3dcdc1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -103,7 +103,7 @@ static void mask_filter_task_cb(void *__restrict userdata, switch (mode) { case MASK_FILTER_SMOOTH: case MASK_FILTER_SHARPEN: { - float val = SCULPT_neighbor_mask_average(ss, vd.index); + float val = SCULPT_neighbor_mask_average(ss, vd.vertex); val -= *vd.mask; @@ -123,7 +123,7 @@ static void mask_filter_task_cb(void *__restrict userdata, } case MASK_FILTER_GROW: max = 0.0f; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vmask_f = data->prev_mask[ni.index]; if (vmask_f > max) { max = vmask_f; @@ -134,7 +134,7 @@ static void mask_filter_task_cb(void *__restrict userdata, break; case MASK_FILTER_SHRINK: min = 1.0f; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vmask_f = data->prev_mask[ni.index]; if (vmask_f < min) { min = vmask_f; @@ -162,9 +162,6 @@ static void mask_filter_task_cb(void *__restrict userdata, if (*vd.mask != prev_val) { update = true; } - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } } BKE_pbvh_vertex_iter_end; @@ -196,7 +193,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) int num_verts = SCULPT_vertex_count_get(ss); BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); - SCULPT_undo_push_begin(ob, "Mask Filter"); + SCULPT_undo_push_begin(ob, op); for (int i = 0; i < totnode; i++) { SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); @@ -217,7 +214,8 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask"); for (int j = 0; j < num_verts; j++) { - prev_mask[j] = SCULPT_vertex_mask_get(ss, j); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, j); + prev_mask[j] = SCULPT_vertex_mask_get(ss, vertex); } } @@ -308,9 +306,9 @@ static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd) zero_v3(avg); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) { float normalized[3]; - sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.index), vd->co); + sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.vertex), vd->co); normalize_v3(normalized); add_v3_v3(avg, normalized); total++; @@ -386,10 +384,6 @@ static void dirty_mask_apply_task_cb(void *__restrict userdata, mask = fminf(mask, 0.5f) * 2.0f; } *vd.mask = CLAMPIS(mask, 0.0f, 1.0f); - - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } } BKE_pbvh_vertex_iter_end; BKE_pbvh_node_mark_update_mask(node); @@ -415,7 +409,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) } BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); - SCULPT_undo_push_begin(ob, "Dirty Mask"); + SCULPT_undo_push_begin(ob, op); for (int i = 0; i < totnode; i++) { SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index dbed5624adf..e576cfda3af 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -281,7 +281,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, const eSculptMeshFilterType filter_type = data->filter_type; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); /* When using the relax face sets meshes filter, * each 3 iterations, do a whole mesh relax to smooth the contents of the Face Set. */ @@ -296,7 +296,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, 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.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); 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 @@ -314,7 +314,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, } if (filter_type == MESH_FILTER_RELAX_FACE_SETS) { - if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) { + if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) { continue; } } @@ -322,7 +322,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, switch (filter_type) { case MESH_FILTER_SMOOTH: fade = clamp_f(fade, -1.0f, 1.0f); - SCULPT_neighbor_coords_average_interior(ss, avg, vd.index); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex); sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); sub_v3_v3v3(disp, val, orig_co); @@ -385,7 +385,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, disp, vd.co, ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + vd.vertex, orig_data.co, ss->filter_cache->surface_smooth_shape_preservation); break; @@ -399,10 +399,10 @@ static void mesh_filter_task_cb(void *__restrict userdata, float disp_sharpen[3] = {0.0f, 0.0f, 0.0f}; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float disp_n[3]; sub_v3_v3v3( - disp_n, SCULPT_vertex_co_get(ss, ni.index), SCULPT_vertex_co_get(ss, vd.index)); + disp_n, SCULPT_vertex_co_get(ss, ni.vertex), SCULPT_vertex_co_get(ss, vd.vertex)); mul_v3_fl(disp_n, ss->filter_cache->sharpen_factor[ni.index]); add_v3_v3(disp_sharpen, disp_n); } @@ -412,7 +412,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, float disp_avg[3]; float avg_co[3]; - SCULPT_neighbor_coords_average(ss, avg_co, vd.index); + SCULPT_neighbor_coords_average(ss, avg_co, vd.vertex); sub_v3_v3v3(disp_avg, avg_co, vd.co); mul_v3_v3fl( disp_avg, disp_avg, smooth_ratio * pow2f(ss->filter_cache->sharpen_factor[vd.index])); @@ -457,7 +457,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, } copy_v3_v3(vd.co, final_pos); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -473,9 +473,11 @@ static void mesh_filter_enhance_details_init_directions(SculptSession *ss) filter_cache->detail_directions = MEM_malloc_arrayN( totvert, sizeof(float[3]), "detail directions"); for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + SCULPT_neighbor_coords_average(ss, avg, vertex); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -500,7 +502,9 @@ static void mesh_filter_init_limit_surface_co(SculptSession *ss) filter_cache->limit_surface_co = MEM_malloc_arrayN( totvert, sizeof(float[3]), "limit surface co"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_limit_surface_get(ss, vertex, filter_cache->limit_surface_co[i]); } } @@ -520,9 +524,11 @@ static void mesh_filter_sharpen_init(SculptSession *ss, totvert, sizeof(float[3]), "sharpen detail direction"); for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + SCULPT_neighbor_coords_average(ss, avg, vertex); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]); } @@ -544,12 +550,14 @@ static void mesh_filter_sharpen_init(SculptSession *ss, smooth_iterations < filter_cache->sharpen_curvature_smooth_iterations; smooth_iterations++) { for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float direction_avg[3] = {0.0f, 0.0f, 0.0f}; float sharpen_avg = 0; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]); sharpen_avg += filter_cache->sharpen_factor[ni.index]; total++; @@ -576,7 +584,7 @@ static void mesh_filter_surface_smooth_displace_task_cb( 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.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); if (fade == 0.0f) { continue; } @@ -584,7 +592,7 @@ static void mesh_filter_surface_smooth_displace_task_cb( SCULPT_surface_smooth_displace_step(ss, vd.co, ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + vd.vertex, ss->filter_cache->surface_smooth_current_vertex, clamp_f(fade, 0.0f, 1.0f)); } @@ -686,7 +694,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_boundary_info_ensure(ob); } - SCULPT_undo_push_begin(ob, "Mesh Filter"); + SCULPT_undo_push_begin(ob, op); SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.c b/source/blender/editors/sculpt_paint/sculpt_geodesic.c index 1beb5d48961..a5885092ee3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.c +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c @@ -37,7 +37,6 @@ #include "DEG_depsgraph.h" #include "WM_api.h" -#include "WM_message.h" #include "WM_toolsystem.h" #include "WM_types.h" @@ -62,9 +61,9 @@ /* Propagate distance from v1 and v2 to v0. */ static bool sculpt_geodesic_mesh_test_dist_add( - MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_vertices) + MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_verts) { - if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(v0))) { + if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(v0))) { return false; } @@ -97,7 +96,7 @@ static bool sculpt_geodesic_mesh_test_dist_add( } static float *SCULPT_geodesic_mesh_create(Object *ob, - GSet *initial_vertices, + GSet *initial_verts, const float limit_radius) { SculptSession *ss = ob->sculpt; @@ -108,8 +107,10 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, const float limit_radius_sq = limit_radius * limit_radius; - MEdge *edges = mesh->medge; MVert *verts = SCULPT_mesh_deformed_mverts_get(ss); + const MEdge *edges = BKE_mesh_edges(mesh); + const MPoly *polys = BKE_mesh_polys(mesh); + const MLoop *loops = BKE_mesh_loops(mesh); float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances"); BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag"); @@ -117,16 +118,15 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, if (!ss->epmap) { BKE_mesh_edge_poly_map_create(&ss->epmap, &ss->epmap_mem, - mesh->medge, + edges, mesh->totedge, - mesh->mpoly, + polys, mesh->totpoly, - mesh->mloop, + loops, mesh->totloop); } if (!ss->vemap) { - BKE_mesh_vert_edge_map_create( - &ss->vemap, &ss->vemap_mem, mesh->medge, mesh->totvert, mesh->totedge); + BKE_mesh_vert_edge_map_create(&ss->vemap, &ss->vemap_mem, edges, mesh->totvert, mesh->totedge); } /* Both contain edge indices encoded as *void. */ @@ -137,7 +137,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, BLI_LINKSTACK_INIT(queue_next); for (int i = 0; i < totvert; i++) { - if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) { + if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(i))) { dists[i] = 0.0f; } else { @@ -159,7 +159,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When * this optimization is needed, it is expected for the tool to request the distance to a low * number of vertices (usually just 1 or 2). */ - GSET_ITER (gs_iter, initial_vertices) { + GSET_ITER (gs_iter, initial_verts) { const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); float *v_co = verts[v].co; for (int i = 0; i < totvert; i++) { @@ -193,7 +193,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, SWAP(int, v1, v2); } sculpt_geodesic_mesh_test_dist_add( - verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_vertices); + verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_verts); } if (ss->epmap[e].count != 0) { @@ -202,16 +202,15 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, if (ss->face_sets[poly] <= 0) { continue; } - const MPoly *mpoly = &mesh->mpoly[poly]; + const MPoly *mpoly = &polys[poly]; for (int loop_index = 0; loop_index < mpoly->totloop; loop_index++) { - const MLoop *mloop = &mesh->mloop[loop_index + mpoly->loopstart]; + const MLoop *mloop = &loops[loop_index + mpoly->loopstart]; const int v_other = mloop->v; if (ELEM(v_other, v1, v2)) { continue; } - if (sculpt_geodesic_mesh_test_dist_add( - verts, v_other, v1, v2, dists, initial_vertices)) { + if (sculpt_geodesic_mesh_test_dist_add(verts, v_other, v1, v2, dists, initial_verts)) { for (int edge_map_index = 0; edge_map_index < ss->vemap[v_other].count; edge_map_index++) { const int e_other = ss->vemap[v_other].indices[edge_map_index]; @@ -258,7 +257,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, /* For sculpt mesh data that does not support a geodesic distances algorithm, fallback to the * distance to each vertex. In this case, only one of the initial vertices will be used to * calculate the distance. */ -static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices) +static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_verts) { SculptSession *ss = ob->sculpt; @@ -267,7 +266,7 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances"); int first_affected = SCULPT_GEODESIC_VERTEX_NONE; GSetIterator gs_iter; - GSET_ITER (gs_iter, initial_vertices) { + GSET_ITER (gs_iter, initial_verts) { first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); break; } @@ -279,25 +278,26 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices return dists; } - const float *first_affected_co = SCULPT_vertex_co_get(ss, first_affected); + const float *first_affected_co = SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, first_affected)); for (int i = 0; i < totvert; i++) { - dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, vertex)); } return dists; } -float *SCULPT_geodesic_distances_create(Object *ob, - GSet *initial_vertices, - const float limit_radius) +float *SCULPT_geodesic_distances_create(Object *ob, GSet *initial_verts, const float limit_radius) { SculptSession *ss = ob->sculpt; switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return SCULPT_geodesic_mesh_create(ob, initial_vertices, limit_radius); + return SCULPT_geodesic_mesh_create(ob, initial_verts, limit_radius); case PBVH_BMESH: case PBVH_GRIDS: - return SCULPT_geodesic_fallback_create(ob, initial_vertices); + return SCULPT_geodesic_fallback_create(ob, initial_verts); } BLI_assert(false); return NULL; @@ -305,16 +305,17 @@ float *SCULPT_geodesic_distances_create(Object *ob, float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, Object *ob, - const int vertex, + const PBVHVertRef vertex, const float limit_radius) { SculptSession *ss = ob->sculpt; - GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); + GSet *initial_verts = BLI_gset_int_new("initial_verts"); const char symm = SCULPT_mesh_symmetry_xyz_get(ob); for (char i = 0; i <= symm; ++i) { if (SCULPT_is_symmetry_iteration_valid(i, symm)) { - int v = -1; + PBVHVertRef v = {PBVH_REF_NONE}; + if (i == 0) { v = vertex; } @@ -323,22 +324,23 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i); v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false); } - if (v != -1) { - BLI_gset_add(initial_vertices, POINTER_FROM_INT(v)); + if (v.i != PBVH_REF_NONE) { + BLI_gset_add(initial_verts, POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ss->pbvh, v))); } } } - float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); - BLI_gset_free(initial_vertices, NULL); + float *dists = SCULPT_geodesic_distances_create(ob, initial_verts, limit_radius); + BLI_gset_free(initial_verts, NULL); return dists; } -float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius) +float *SCULPT_geodesic_from_vertex(Object *ob, const PBVHVertRef vertex, const float limit_radius) { - GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); - BLI_gset_add(initial_vertices, POINTER_FROM_INT(vertex)); - float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); - BLI_gset_free(initial_vertices, NULL); + GSet *initial_verts = BLI_gset_int_new("initial_verts"); + BLI_gset_add(initial_verts, + POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ob->sculpt->pbvh, vertex))); + float *dists = SCULPT_geodesic_distances_create(ob, initial_verts, limit_radius); + BLI_gset_free(initial_verts, NULL); return dists; } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 0693b445fe5..4bc06d68a02 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -59,10 +59,13 @@ typedef struct SculptCursorGeometryInfo { typedef struct SculptVertexNeighborIter { /* Storage */ - int *neighbors; + PBVHVertRef *neighbors; + int *neighbor_indices; int size; int capacity; - int neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + + PBVHVertRef neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + int neighbor_indices_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; /* Internal iterator. */ int num_duplicates; @@ -70,6 +73,7 @@ typedef struct SculptVertexNeighborIter { /* Public */ int index; + PBVHVertRef vertex; bool is_duplicate; } SculptVertexNeighborIter; @@ -93,7 +97,7 @@ typedef struct { /* Flood Fill. */ typedef struct { GSQueue *queue; - BLI_bitmap *visited_vertices; + BLI_bitmap *visited_verts; } SculptFloodFill; typedef enum eBoundaryAutomaskMode { @@ -247,7 +251,7 @@ typedef struct SculptThreadedTaskData { float (*mat)[4]; float (*vertCos)[3]; - /* When true, the displacement stored in the proxies will be aplied to the original coordinates + /* When true, the displacement stored in the proxies will be applied to the original coordinates * instead of to the current coordinates. */ bool use_proxies_orco; @@ -318,7 +322,7 @@ typedef struct SculptThreadedTaskData { bool mask_by_color_preserve_mask; /* Index of the vertex that is going to be used as a reference for the colors. */ - int mask_by_color_vertex; + PBVHVertRef mask_by_color_vertex; float *mask_by_color_floodfill; int face_set; @@ -485,6 +489,7 @@ typedef struct StrokeCache { float true_last_location[3]; float location[3]; float last_location[3]; + float stroke_distance; /* Used for alternating between deformation in brushes that need to apply different ones to * achieve certain effects. */ @@ -690,7 +695,8 @@ typedef struct ExpandCache { * during the execution of Expand by moving the origin. */ float initial_mouse_move[2]; float initial_mouse[2]; - int initial_active_vertex; + PBVHVertRef initial_active_vertex; + int initial_active_vertex_i; int initial_active_face_set; /* Maximum number of vertices allowed in the SculptSession for previewing the falloff using @@ -845,7 +851,10 @@ void SCULPT_tag_update_overlays(bContext *C); * (This allows us to ignore the GL depth buffer) * Returns 0 if the ray doesn't hit the mesh, non-zero otherwise. */ -bool SCULPT_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]); +bool SCULPT_stroke_get_location(struct bContext *C, + float out[3], + const float mouse[2], + bool force_original); /** * Gets the normal, location and active vertex location of the geometry under the cursor. This also * updates the active vertex and cursor related data of the SculptSession using the mouse position @@ -858,7 +867,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, struct SculptSession *ss, void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush); float SCULPT_raycast_init(struct ViewContext *vc, - const float mouse[2], + const float mval[2], float ray_start[3], float ray_end[3], float ray_normal[3], @@ -895,14 +904,14 @@ bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(struct StrokeCache *cach void SCULPT_vertex_random_access_ensure(struct SculptSession *ss); int SCULPT_vertex_count_get(struct SculptSession *ss); -const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index); +const float *SCULPT_vertex_co_get(struct SculptSession *ss, PBVHVertRef vertex); /** Get the normal for a given sculpt vertex; do not modify the result */ -void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]); +void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); -float SCULPT_vertex_mask_get(struct SculptSession *ss, int index); -void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4]); -void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4]); +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]); /** Returns true if a color attribute exists in the current sculpt session. */ bool SCULPT_has_colors(const SculptSession *ss); @@ -910,19 +919,19 @@ bool SCULPT_has_colors(const SculptSession *ss); /** Returns true if the active color attribute is on loop (ATTR_DOMAIN_CORNER) domain. */ bool SCULPT_has_loop_colors(const struct Object *ob); -const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index); -void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]); +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex); +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); /** * Coordinates used for manipulating the base mesh when Grab Active Vertex is enabled. */ -const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index); +const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, PBVHVertRef vertex); /** * Returns the info of the limit surface when multi-res is available, * otherwise it returns the current coordinate of the vertex. */ -void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]); +void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, float r_co[3]); /** * Returns the pointer to the coordinates that should be edited from a brush tool iterator @@ -933,7 +942,7 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, PBVHVertexIter *iter); void SCULPT_vertex_neighbors_get(struct SculptSession *ss, - int index, + PBVHVertRef vertex, bool include_duplicates, SculptVertexNeighborIter *iter); @@ -942,7 +951,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss, SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \ for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ neighbor_iterator.i++) { \ - neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i]; + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; /** Iterate over neighboring and duplicate vertices (for PBVH_GRIDS). Duplicates come * first since they are nearest for floodfill. */ @@ -950,7 +960,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss, SCULPT_vertex_neighbors_get(ss, v_index, true, &neighbor_iterator); \ for (neighbor_iterator.i = neighbor_iterator.size - 1; neighbor_iterator.i >= 0; \ neighbor_iterator.i--) { \ - neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; \ neighbor_iterator.is_duplicate = (neighbor_iterator.i >= \ neighbor_iterator.size - neighbor_iterator.num_duplicates); @@ -961,7 +972,7 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss, } \ ((void)0) -int SCULPT_active_vertex_get(SculptSession *ss); +PBVHVertRef SCULPT_active_vertex_get(SculptSession *ss); const float *SCULPT_active_vertex_co_get(SculptSession *ss); void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]); @@ -981,7 +992,7 @@ void SCULPT_fake_neighbors_free(struct Object *ob); /* Vertex Info. */ void SCULPT_boundary_info_ensure(Object *object); /* Boundary Info needs to be initialized in order to use this function. */ -bool SCULPT_vertex_is_boundary(const SculptSession *ss, int index); +bool SCULPT_vertex_is_boundary(const SculptSession *ss, PBVHVertRef vertex); void SCULPT_connected_components_ensure(Object *ob); @@ -991,10 +1002,10 @@ void SCULPT_connected_components_ensure(Object *ob); /** \name Sculpt Visibility API * \{ */ -void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible); -bool SCULPT_vertex_visible_get(SculptSession *ss, int index); +void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible); +bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex); -void SCULPT_visibility_sync_all_face_sets_to_vertices(struct Object *ob); +void SCULPT_visibility_sync_all_face_sets_to_verts(struct Object *ob); void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); /** \} */ @@ -1004,17 +1015,17 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); * \{ */ int SCULPT_active_face_set_get(SculptSession *ss); -int SCULPT_vertex_face_set_get(SculptSession *ss, int index); -void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set); +int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex); +void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set); -bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set); -bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index); +bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set); +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, int index); -bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index); +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); @@ -1029,7 +1040,10 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible); * Initialize a #SculptOrigVertData for accessing original vertex data; * handles #BMesh, #Mesh, and multi-resolution. */ -void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node); +void SCULPT_orig_vert_data_init(SculptOrigVertData *data, + Object *ob, + PBVHNode *node, + SculptUndoType type); /** * Update a #SculptOrigVertData for a particular vertex from the PBVH iterator. */ @@ -1097,11 +1111,11 @@ void SCULPT_calc_area_normal_and_center( void SCULPT_calc_area_center( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]); -int SCULPT_nearest_vertex_get(struct Sculpt *sd, - struct Object *ob, - const float co[3], - float max_distance, - bool use_original); +PBVHVertRef SCULPT_nearest_vertex_get(struct Sculpt *sd, + struct Object *ob, + const float co[3], + float max_distance, + bool use_original); int SCULPT_plane_point_side(const float co[3], const float plane[4]); int SCULPT_plane_trim(const struct StrokeCache *cache, @@ -1180,7 +1194,7 @@ float SCULPT_brush_strength_factor(struct SculptSession *ss, const float vno[3], const float fno[3], float mask, - int vertex_index, + const PBVHVertRef vertex, int thread_id); /** @@ -1211,15 +1225,18 @@ void SCULPT_floodfill_add_initial_with_symmetry(struct Sculpt *sd, struct Object *ob, struct SculptSession *ss, SculptFloodFill *flood, - int index, + PBVHVertRef vertex, float radius); -void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index); -void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index); -void SCULPT_floodfill_execute( - struct SculptSession *ss, - SculptFloodFill *flood, - bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata), - void *userdata); +void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex); +void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, PBVHVertRef vertex); +void SCULPT_floodfill_execute(struct SculptSession *ss, + SculptFloodFill *flood, + bool (*func)(SculptSession *ss, + PBVHVertRef from_v, + PBVHVertRef to_v, + bool is_duplicate, + void *userdata), + void *userdata); void SCULPT_floodfill_free(SculptFloodFill *flood); /** \} */ @@ -1269,7 +1286,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking, SculptSession *ss, - int vert); + PBVHVertRef vertex); /* Returns the automasking cache depending on the active tool. Used for code that can run both for * brushes and filter. */ @@ -1302,9 +1319,9 @@ float *SCULPT_geodesic_distances_create(struct Object *ob, float limit_radius); float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd, struct Object *ob, - int vertex, + PBVHVertRef vertex, float limit_radius); -float *SCULPT_geodesic_from_vertex(Object *ob, int vertex, float limit_radius); +float *SCULPT_geodesic_from_vertex(Object *ob, PBVHVertRef vertex, float limit_radius); /** \} */ /* -------------------------------------------------------------------- */ @@ -1338,7 +1355,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim); /* Public functions. */ -struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct SculptSession *ss, +struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct Object *ob, float cloth_mass, float cloth_damping, float cloth_softbody_strength, @@ -1410,14 +1427,16 @@ BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush) */ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct BMVert *v); -void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index); -float SCULPT_neighbor_mask_average(SculptSession *ss, int index); -void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index); +void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], PBVHVertRef vertex); +float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex); +void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef vertex); /** * Mask the mesh boundaries smoothing only the mesh surface without using auto-masking. */ -void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index); +void SCULPT_neighbor_coords_average_interior(SculptSession *ss, + float result[3], + PBVHVertRef vertex); void SCULPT_smooth( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength, bool smooth_mask); @@ -1429,11 +1448,15 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], float (*laplacian_disp)[3], - int v_index, + PBVHVertRef vertex, const float origco[3], float alpha); -void SCULPT_surface_smooth_displace_step( - SculptSession *ss, float *co, float (*laplacian_disp)[3], int v_index, float beta, float fade); +void SCULPT_surface_smooth_displace_step(SculptSession *ss, + float *co, + float (*laplacian_disp)[3], + PBVHVertRef vertex, + float beta, + float fade); void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); /* Slide/Relax */ @@ -1467,14 +1490,21 @@ void SCULPT_cache_free(StrokeCache *cache); * \{ */ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type); -SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node); +SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type); SculptUndoNode *SCULPT_undo_get_first_node(void); /** - * NOTE: `name` must match operator name for - * redo panels to work. + * Pushes an undo step using the operator name. This is necessary for + * redo panels to work; operators that do not support that may use + * #SCULPT_undo_push_begin_ex instead if so desired. */ -void SCULPT_undo_push_begin(struct Object *ob, const char *name); +void SCULPT_undo_push_begin(struct Object *ob, const struct wmOperator *op); + +/** + * NOTE: #SCULPT_undo_push_begin is preferred since `name` + * must match operator name for redo panels to work. + */ +void SCULPT_undo_push_begin_ex(struct Object *ob, const char *name); void SCULPT_undo_push_end(struct Object *ob); void SCULPT_undo_push_end_ex(struct Object *ob, const bool use_nested_undo); @@ -1639,7 +1669,7 @@ void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain); */ struct SculptBoundary *SCULPT_boundary_data_init(Object *object, Brush *brush, - int initial_vertex, + PBVHVertRef initial_vertex, float radius); void SCULPT_boundary_data_free(struct SculptBoundary *boundary); /* Main Brush Function. */ @@ -1802,7 +1832,10 @@ void SCULPT_OT_brush_stroke(struct wmOperatorType *ot); /* end sculpt_ops.c */ -#define SCULPT_TOOL_NEEDS_COLOR(tool) ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) +BLI_INLINE bool SCULPT_tool_is_paint(int tool) +{ + return ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR); +} #ifdef __cplusplus } diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 4593c6a8b60..9556d24f12c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -97,11 +97,14 @@ static void sculpt_expand_task_cb(void *__restrict userdata, PBVHVertexIter vd; int update_it = data->mask_expand_update_it; + PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); + int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex); + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { int vi = vd.index; float final_mask = *vd.mask; if (data->mask_expand_use_normals) { - if (ss->filter_cache->normal_factor[SCULPT_active_vertex_get(ss)] < + if (ss->filter_cache->normal_factor[active_vertex_i] < ss->filter_cache->normal_factor[vd.index]) { final_mask = 1.0f; } @@ -121,7 +124,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata, if (data->mask_expand_create_face_set) { if (final_mask == 1.0f) { - SCULPT_vertex_face_set_set(ss, vd.index, ss->filter_cache->new_face_set); + SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set); } BKE_pbvh_node_mark_redraw(node); } @@ -136,9 +139,6 @@ static void sculpt_expand_task_cb(void *__restrict userdata, } if (*vd.mask != final_mask) { - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } *vd.mask = final_mask; BKE_pbvh_node_mark_update_mask(node); } @@ -167,10 +167,13 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * if (RNA_boolean_get(op->ptr, "use_cursor")) { SculptCursorGeometryInfo sgi; + const float mval_fl[2] = {UNPACK2(event->mval)}; if (SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) { + int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, SCULPT_active_vertex_get(ss)); + /* The cursor is over the mesh, get the update iteration from the updated active vertex. */ - mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)]; + mask_expand_update_it = ss->filter_cache->mask_update_it[active_vertex_i]; } else { /* When the cursor is outside the mesh, affect the entire connected component. */ @@ -291,13 +294,16 @@ typedef struct MaskExpandFloodFillData { } MaskExpandFloodFillData; static bool mask_expand_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { MaskExpandFloodFillData *data = 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); + if (!is_duplicate) { - int to_it = ss->filter_cache->mask_update_it[from_v] + 1; - ss->filter_cache->mask_update_it[to_v] = to_it; + int to_it = ss->filter_cache->mask_update_it[from_v_i] + 1; + ss->filter_cache->mask_update_it[to_v_i] = to_it; if (to_it > ss->filter_cache->mask_update_last_it) { ss->filter_cache->mask_update_last_it = to_it; } @@ -306,20 +312,20 @@ static bool mask_expand_floodfill_cb( float current_normal[3], prev_normal[3]; SCULPT_vertex_normal_get(ss, to_v, current_normal); SCULPT_vertex_normal_get(ss, from_v, prev_normal); - const float from_edge_factor = ss->filter_cache->edge_factor[from_v]; - ss->filter_cache->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * - from_edge_factor; - ss->filter_cache->normal_factor[to_v] = dot_v3v3(data->original_normal, current_normal) * - powf(from_edge_factor, data->edge_sensitivity); - CLAMP(ss->filter_cache->normal_factor[to_v], 0.0f, 1.0f); + const float from_edge_factor = ss->filter_cache->edge_factor[from_v_i]; + ss->filter_cache->edge_factor[to_v_i] = dot_v3v3(current_normal, prev_normal) * + from_edge_factor; + ss->filter_cache->normal_factor[to_v_i] = dot_v3v3(data->original_normal, current_normal) * + powf(from_edge_factor, data->edge_sensitivity); + CLAMP(ss->filter_cache->normal_factor[to_v_i], 0.0f, 1.0f); } } else { /* PBVH_GRIDS duplicate handling. */ - ss->filter_cache->mask_update_it[to_v] = ss->filter_cache->mask_update_it[from_v]; + ss->filter_cache->mask_update_it[to_v_i] = ss->filter_cache->mask_update_it[from_v_i]; if (data->use_normals) { - ss->filter_cache->edge_factor[to_v] = ss->filter_cache->edge_factor[from_v]; - ss->filter_cache->normal_factor[to_v] = ss->filter_cache->normal_factor[from_v]; + ss->filter_cache->edge_factor[to_v_i] = ss->filter_cache->edge_factor[from_v_i]; + ss->filter_cache->normal_factor[to_v_i] = ss->filter_cache->normal_factor[from_v_i]; } } @@ -355,7 +361,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent BKE_pbvh_search_gather(pbvh, NULL, NULL, &ss->filter_cache->nodes, &ss->filter_cache->totnode); - SCULPT_undo_push_begin(ob, "Mask Expand"); + SCULPT_undo_push_begin(ob, op); if (create_face_set) { SCULPT_undo_push_node(ob, ss->filter_cache->nodes[0], SCULPT_UNDO_FACE_SETS); @@ -392,13 +398,17 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent else { ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask"); for (int i = 0; i < vertex_count; i++) { - ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, i); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, vertex); } } + int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, SCULPT_active_vertex_get(ss)); + ss->filter_cache->mask_update_last_it = 1; ss->filter_cache->mask_update_current_it = 1; - ss->filter_cache->mask_update_it[SCULPT_active_vertex_get(ss)] = 0; + ss->filter_cache->mask_update_it[active_vertex_i] = 0; copy_v3_v3(ss->filter_cache->mask_expand_initial_co, SCULPT_active_vertex_co_get(ss)); @@ -417,9 +427,11 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent if (use_normals) { for (int repeat = 0; repeat < 2; repeat++) { for (int i = 0; i < vertex_count; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg = 0.0f; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { avg += ss->filter_cache->normal_factor[ni.index]; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c index 025f34ab2d7..b9b889ab2ce 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c @@ -99,7 +99,7 @@ static void mask_init_task_cb(void *__restrict userdata, *vd.mask = BLI_hash_int_01(vd.index + seed); break; case SCULPT_MASK_INIT_RANDOM_PER_FACE_SET: { - const int face_set = SCULPT_vertex_face_set_get(ss, vd.index); + const int face_set = SCULPT_vertex_face_set_get(ss, vd.vertex); *vd.mask = BLI_hash_int_01(face_set + seed); break; } @@ -131,7 +131,7 @@ static int sculpt_mask_init_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - SCULPT_undo_push_begin(ob, "init mask"); + SCULPT_undo_push_begin(ob, op); if (mode == SCULPT_MASK_INIT_RANDOM_PER_LOOSE_PART) { SCULPT_connected_components_ensure(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index ddc1a0e1db0..1e8731e54c0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -86,7 +86,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); /* Sample the normal and area of the +X and -X axis individually. */ @@ -194,13 +194,13 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index f16763be735..10a2ece73de 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -47,6 +47,7 @@ #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" @@ -129,8 +130,10 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) "layer persistent base"); for (int i = 0; i < totvert; i++) { - copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i)); - SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no); + 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; } @@ -213,7 +216,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) * as deleted, then after symmetrize operation all BMesh elements * are logged as added (as opposed to attempting to store just the * parts that symmetrize modifies). */ - SCULPT_undo_push_begin(ob, "Dynamic topology symmetrize"); + SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE); BM_log_before_all_removed(ss->bm, ss->bm_log); @@ -240,7 +243,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) break; case PBVH_FACES: /* Mesh Symmetrize. */ - ED_sculpt_undo_geometry_begin(ob, "mesh symmetrize"); + ED_sculpt_undo_geometry_begin(ob, op); Mesh *mesh = ob->data; BKE_mesh_mirror_apply_mirror_on_axis(bmain, mesh, sd->symmetrize_direction, dist); @@ -392,7 +395,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain, bool has_undo = wm->undo_stack != NULL; /* Undo push is needed to prevent memory leak. */ if (has_undo) { - SCULPT_undo_push_begin(ob, "Dynamic topology enable"); + SCULPT_undo_push_begin_ex(ob, "Dynamic topology enable"); } SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); if (has_undo) { @@ -416,7 +419,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); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); } @@ -468,7 +471,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); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); ED_object_sculptmode_exit_ex(bmain, depsgraph, scene, ob); } @@ -480,7 +483,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); - Object *ob = OBACT(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; @@ -508,7 +511,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) * while it works it causes lag when undoing the first undo step, see T71564. */ wmWindowManager *wm = CTX_wm_manager(C); if (wm->op_undo_depth <= 1) { - SCULPT_undo_push_begin(ob, op->type->name); + SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_end(ob); } } @@ -543,7 +546,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob = CTX_data_active_object(C); - ss->preview_vert_index_count = 0; + ss->preview_vert_count = 0; int totpoints = 0; /* This function is called from the cursor drawing code, so the PBVH may not be build yet. */ @@ -568,193 +571,50 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float float brush_co[3]; copy_v3_v3(brush_co, SCULPT_active_vertex_co_get(ss)); - BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices"); + BLI_bitmap *visited_verts = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_verts"); /* Assuming an average of 6 edges per vertex in a triangulated mesh. */ - const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2; + const int max_preview_verts = SCULPT_vertex_count_get(ss) * 3 * 2; - if (ss->preview_vert_index_list == NULL) { - ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines"); + if (ss->preview_vert_list == NULL) { + ss->preview_vert_list = MEM_callocN(max_preview_verts * sizeof(PBVHVertRef), "preview lines"); } - GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int)); - int active_v = SCULPT_active_vertex_get(ss); - BLI_gsqueue_push(not_visited_vertices, &active_v); + GSQueue *non_visited_verts = BLI_gsqueue_new(sizeof(PBVHVertRef)); + PBVHVertRef active_v = SCULPT_active_vertex_get(ss); + BLI_gsqueue_push(non_visited_verts, &active_v); + + while (!BLI_gsqueue_is_empty(non_visited_verts)) { + PBVHVertRef from_v; - while (!BLI_gsqueue_is_empty(not_visited_vertices)) { - int from_v; - BLI_gsqueue_pop(not_visited_vertices, &from_v); + BLI_gsqueue_pop(non_visited_verts, &from_v); SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - if (totpoints + (ni.size * 2) < max_preview_vertices) { - int to_v = ni.index; - ss->preview_vert_index_list[totpoints] = from_v; + if (totpoints + (ni.size * 2) < max_preview_verts) { + PBVHVertRef to_v = ni.vertex; + int to_v_i = ni.index; + ss->preview_vert_list[totpoints] = from_v; totpoints++; - ss->preview_vert_index_list[totpoints] = to_v; + ss->preview_vert_list[totpoints] = to_v; totpoints++; - if (BLI_BITMAP_TEST(visited_vertices, to_v)) { + if (BLI_BITMAP_TEST(visited_verts, to_v_i)) { continue; } - BLI_BITMAP_ENABLE(visited_vertices, to_v); + BLI_BITMAP_ENABLE(visited_verts, to_v_i); const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v); if (len_squared_v3v3(brush_co, co) < radius * radius) { - BLI_gsqueue_push(not_visited_vertices, &to_v); + BLI_gsqueue_push(non_visited_verts, &to_v); } } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); } - BLI_gsqueue_free(not_visited_vertices); + BLI_gsqueue_free(non_visited_verts); - MEM_freeN(visited_vertices); + MEM_freeN(visited_verts); - ss->preview_vert_index_count = totpoints; -} - -static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_active_object(C); - - ID *data; - data = ob->data; - if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) { - return OPERATOR_CANCELLED; - } - - if (ob->type != OB_MESH) { - return OPERATOR_CANCELLED; - } - - Mesh *mesh = ob->data; - - const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR); - if (mloopcol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); - - const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); - if (MPropCol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - const MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - - const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); - - for (int i = 0; i < mesh->totpoly; i++) { - const MPoly *c_poly = &polys[i]; - for (int j = 0; j < c_poly->totloop; j++) { - int loop_index = c_poly->loopstart + j; - const MLoop *c_loop = &loops[c_poly->loopstart + j]; - float srgb_color[4]; - linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color); - loopcols[loop_index].r = (char)(srgb_color[0] * 255); - loopcols[loop_index].g = (char)(srgb_color[1] * 255); - loopcols[loop_index].b = (char)(srgb_color[2] * 255); - loopcols[loop_index].a = (char)(srgb_color[3] * 255); - } - } - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - - return OPERATOR_FINISHED; -} - -static bool sculpt_colors_poll(bContext *C) -{ - if (!SCULPT_mode_poll(C)) { - return false; - } - - Object *ob = CTX_data_active_object(C); - - if (!ob->sculpt || !ob->sculpt->pbvh || BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_FACES) { - return false; - } - - return SCULPT_has_colors(ob->sculpt); -} - -static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Sculpt Vertex Color to Vertex Color"; - ot->description = "Copy the Sculpt Vertex Color to a regular color layer"; - ot->idname = "SCULPT_OT_vertex_to_loop_colors"; - - /* api callbacks */ - ot->poll = sculpt_colors_poll; - ot->exec = vertex_to_loop_colors_exec; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_active_object(C); - - ID *data; - data = ob->data; - if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) { - return OPERATOR_CANCELLED; - } - - if (ob->type != OB_MESH) { - return OPERATOR_CANCELLED; - } - - Mesh *mesh = ob->data; - - const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR); - if (mloopcol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - const MLoopCol *loopcols = CustomData_get_layer_n( - &mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); - - const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); - if (MPropCol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - - const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); - - for (int i = 0; i < mesh->totpoly; i++) { - const MPoly *c_poly = &polys[i]; - for (int j = 0; j < c_poly->totloop; j++) { - int loop_index = c_poly->loopstart + j; - const MLoop *c_loop = &loops[c_poly->loopstart + j]; - vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f); - vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f); - vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f); - vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f); - srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color); - } - } - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Vertex Color to Sculpt Vertex Color"; - ot->description = "Copy the active loop color layer to the vertex color"; - ot->idname = "SCULPT_OT_loop_to_vertex_colors"; - - /* api callbacks */ - ot->poll = sculpt_colors_poll; - ot->exec = loop_to_vertex_colors_exec; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ss->preview_vert_count = totpoints; } static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) @@ -764,7 +624,7 @@ static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent Object *ob = CTX_data_active_object(C); Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; - int active_vertex = SCULPT_active_vertex_get(ss); + PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); float active_vertex_color[4]; if (!SCULPT_handles_colors_report(ss, op->reports)) { @@ -882,9 +742,6 @@ static void do_mask_by_color_contiguous_update_nodes_cb( continue; } update_node = true; - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } } BKE_pbvh_vertex_iter_end; if (update_node) { @@ -893,8 +750,11 @@ static void do_mask_by_color_contiguous_update_nodes_cb( } static bool sculpt_mask_by_color_contiguous_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *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); + MaskByColorContiguousFloodFillData *data = userdata; float current_color[4]; @@ -902,10 +762,10 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb( float new_vertex_mask = sculpt_mask_by_color_delta_get( current_color, data->initial_color, data->threshold, data->invert); - data->new_mask[to_v] = new_vertex_mask; + data->new_mask[to_v_i] = new_vertex_mask; if (is_duplicate) { - data->new_mask[to_v] = data->new_mask[from_v]; + data->new_mask[to_v_i] = data->new_mask[from_v_i]; } float len = len_v3v3(current_color, data->initial_color); @@ -914,7 +774,7 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb( } static void sculpt_mask_by_color_contiguous(Object *object, - const int vertex, + const PBVHVertRef vertex, const float threshold, const bool invert, const bool preserve_mask) @@ -991,7 +851,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); const float current_mask = *vd.mask; const float new_mask = sculpt_mask_by_color_delta_get(active_color, col, threshold, invert); @@ -1001,18 +861,15 @@ static void do_mask_by_color_task_cb(void *__restrict userdata, continue; } update_node = true; - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } } BKE_pbvh_vertex_iter_end; if (update_node) { - BKE_pbvh_node_mark_redraw(data->nodes[n]); + BKE_pbvh_node_mark_update_mask(data->nodes[n]); } } static void sculpt_mask_by_color_full_mesh(Object *object, - const int vertex, + const PBVHVertRef vertex, const float threshold, const bool invert, const bool preserve_mask) @@ -1064,10 +921,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven const float mval_fl[2] = {UNPACK2(event->mval)}; SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); - SCULPT_undo_push_begin(ob, "Mask by color"); + SCULPT_undo_push_begin(ob, op); BKE_sculpt_color_layer_create_if_needed(ob); - const int active_vertex = SCULPT_active_vertex_get(ss); + const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); const float threshold = RNA_float_get(op->ptr, "threshold"); const bool invert = RNA_boolean_get(op->ptr, "invert"); const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask"); @@ -1156,8 +1013,6 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_project_line_gesture); WM_operatortype_append(SCULPT_OT_sample_color); - WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors); - WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors); WM_operatortype_append(SCULPT_OT_color_filter); WM_operatortype_append(SCULPT_OT_mask_by_color); WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit); diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index fa9f24377da..c494c71f1eb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -17,6 +17,7 @@ #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" @@ -80,20 +81,16 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float smooth_color[4]; - SCULPT_neighbor_color_average(ss, smooth_color, vd.index); + SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); blend_color_interpolate_float(col, col, smooth_color, fade); - SCULPT_vertex_color_set(ss, vd.index, col); - - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } + SCULPT_vertex_color_set(ss, vd.vertex, col); } BKE_pbvh_vertex_iter_end; } @@ -111,7 +108,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, PBVHColorBufferNode *color_buffer; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]); @@ -121,11 +118,31 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + copy_v3_v3(brush_color, ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) : BKE_brush_color_get(ss->scene, brush)); + IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color); + if (brush->flag & BRUSH_USE_GRADIENT) { + switch (brush->gradient_stroke_mode) { + case BRUSH_GRADIENT_PRESSURE: + BKE_colorband_evaluate(brush->gradient, ss->cache->pressure, brush_color); + break; + case BRUSH_GRADIENT_SPACING_REPEAT: { + float coord = fmod(ss->cache->stroke_distance / brush->gradient_spacing, 1.0); + BKE_colorband_evaluate(brush->gradient, coord, brush_color); + break; + } + case BRUSH_GRADIENT_SPACING_CLAMP: { + BKE_colorband_evaluate( + brush->gradient, ss->cache->stroke_distance / brush->gradient_spacing, brush_color); + break; + } + } + } + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); @@ -151,7 +168,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); /* Density. */ @@ -182,14 +199,10 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha); float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); IMB_blend_color_float(col, orig_data.col, buffer_color, brush->blend); CLAMP4(col, 0.0f, 1.0f); - SCULPT_vertex_color_set(ss, vd.index, col); - - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } + SCULPT_vertex_color_set(ss, vd.vertex, col); } BKE_pbvh_vertex_iter_end; } @@ -221,7 +234,7 @@ static void do_sample_wet_paint_task_cb(void *__restrict userdata, } float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); add_v4_v4(swptd->color, col); swptd->tot_samples++; @@ -400,7 +413,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float current_disp[3]; @@ -409,7 +422,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]); float no[3]; - SCULPT_vertex_normal_get(ss, vd.index, no); + SCULPT_vertex_normal_get(ss, vd.vertex, no); switch (brush->smear_deform_type) { case BRUSH_SMEAR_DEFORM_DRAG: @@ -427,7 +440,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, madd_v3_v3fl(current_disp, no, -dot_v3v3(current_disp, no)); normalize_v3_v3(current_disp_norm, current_disp); - mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); + mul_v3_v3fl(current_disp, current_disp_norm, bstrength); float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float totw = 0.0f; @@ -442,11 +455,11 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, */ SculptVertexNeighborIter ni2; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni2) { - const float *nco = SCULPT_vertex_co_get(ss, ni2.index); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni2) { + const float *nco = SCULPT_vertex_co_get(ss, ni2.vertex); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.vertex, ni) { if (ni.index == vd.index) { continue; } @@ -454,15 +467,15 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, float vertex_disp[3]; float vertex_disp_norm[3]; - sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); + sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co); /* Weight by how close we are to our target distance from vd.co. */ - float w = (1.0f + fabsf(len_v3(vertex_disp) / ss->cache->bstrength - 1.0f)); + float w = (1.0f + fabsf(len_v3(vertex_disp) / bstrength - 1.0f)); /* TODO: use cotangents (or at least face areas) here. */ - float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.index), nco); + float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.vertex), nco); if (len > 0.0f) { - len = ss->cache->bstrength / len; + len = bstrength / len; } else { /* Coincident point. */ len = 1.0f; @@ -502,13 +515,9 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, blend_color_mix_float(interp_color, interp_color, accum); float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); blend_color_interpolate_float(col, ss->cache->prev_colors[vd.index], interp_color, fade); - SCULPT_vertex_color_set(ss, vd.index, col); - - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); - } + SCULPT_vertex_color_set(ss, vd.vertex, col); } BKE_pbvh_vertex_iter_end; } @@ -522,7 +531,7 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_vertex_color_get(ss, vd.index, ss->cache->prev_colors[vd.index]); + SCULPT_vertex_color_get(ss, vd.vertex, ss->cache->prev_colors[vd.index]); } BKE_pbvh_vertex_iter_end; } @@ -532,7 +541,7 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; - if (!SCULPT_has_colors(ss)) { + if (!SCULPT_has_colors(ss) || ss->cache->bstrength == 0.0f) { return; } @@ -541,7 +550,9 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode if (!ss->cache->prev_colors) { ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_color_get(ss, vertex, ss->cache->prev_colors[i]); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index 975a8f21aaf..8a3a3fe7adc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -172,7 +172,15 @@ template<typename ImageBuffer> class PaintingKernel { 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, pixel_pos, sqrtf(test.dist), normal, face_normal, mask, 0, thread_id); + ss, + brush, + pixel_pos, + sqrtf(test.dist), + normal, + face_normal, + mask, + BKE_pbvh_make_vref(PBVH_REF_NONE), + thread_id); float4 paint_color = brush_color * falloff_strength * brush_strength; float4 buffer_color; blend_color_mix_float(buffer_color, color, paint_color); @@ -383,7 +391,7 @@ static void push_undo(const NodeData &node_data, continue; } int tilex, tiley, tilew, tileh; - ListBase *undo_tiles = ED_image_paint_tile_list_get(); + PaintTileMap *undo_tiles = ED_image_paint_tile_map_get(); undo_region_tiles(&image_buffer, tile_undo.region.xmin, tile_undo.region.ymin, diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index 666cdd3fe50..d1418c8dc35 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -156,7 +156,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, float final_pos[3]; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); @@ -182,7 +182,7 @@ 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.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); mul_v3_fl(disp, mask * automask); /* Accumulate the displacement. */ @@ -196,7 +196,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, copy_v3_v3(target_co, final_pos); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -221,7 +221,7 @@ static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata, float max = 0.0f; /* Grow the factor. */ - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vmask_f = data->prev_mask[ni.index]; max = MAX2(vmask_f, max); } @@ -367,7 +367,7 @@ typedef struct PoseFloodFillData { int current_face_set; int next_face_set; int prev_face_set; - int next_vertex; + PBVHVertRef next_vertex; bool next_face_set_found; @@ -397,14 +397,19 @@ typedef struct PoseFloodFillData { int target_face_set; } PoseFloodFillData; -static bool pose_topology_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata) +static bool pose_topology_floodfill_cb(SculptSession *ss, + PBVHVertRef UNUSED(from_v), + PBVHVertRef to_v, + bool is_duplicate, + void *userdata) { + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + PoseFloodFillData *data = userdata; const float *co = SCULPT_vertex_co_get(ss, to_v); if (data->pose_factor) { - data->pose_factor[to_v] = 1.0f; + data->pose_factor[to_v_i] = 1.0f; } if (len_squared_v3v3(data->pose_initial_co, data->fallback_floodfill_origin) < @@ -426,15 +431,19 @@ static bool pose_topology_floodfill_cb( return false; } -static bool pose_face_sets_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata) +static bool pose_face_sets_floodfill_cb(SculptSession *ss, + PBVHVertRef UNUSED(from_v), + PBVHVertRef to_v, + bool is_duplicate, + void *userdata) { PoseFloodFillData *data = userdata; - const int index = to_v; + const int index = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + const PBVHVertRef vertex = to_v; bool visit_next = false; - const float *co = SCULPT_vertex_co_get(ss, index); + const float *co = SCULPT_vertex_co_get(ss, vertex); const bool symmetry_check = SCULPT_check_vertex_pivot_symmetry( co, data->pose_initial_co, data->symm) && !is_duplicate; @@ -448,11 +457,11 @@ static bool pose_face_sets_floodfill_cb( if (sculpt_pose_brush_is_vertex_inside_brush_radius( co, data->pose_initial_co, data->radius, data->symm)) { - const int visited_face_set = SCULPT_vertex_face_set_get(ss, index); + const int visited_face_set = SCULPT_vertex_face_set_get(ss, vertex); BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(visited_face_set)); } else if (symmetry_check) { - data->current_face_set = SCULPT_vertex_face_set_get(ss, index); + data->current_face_set = SCULPT_vertex_face_set_get(ss, vertex); BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(data->current_face_set)); } return true; @@ -466,11 +475,11 @@ static bool pose_face_sets_floodfill_cb( GSetIterator gs_iter; GSET_ITER (gs_iter, data->visited_face_sets) { const int visited_face_set = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); - is_vertex_valid |= SCULPT_vertex_has_face_set(ss, index, visited_face_set); + is_vertex_valid |= SCULPT_vertex_has_face_set(ss, vertex, visited_face_set); } } else { - is_vertex_valid = SCULPT_vertex_has_face_set(ss, index, data->current_face_set); + is_vertex_valid = SCULPT_vertex_has_face_set(ss, vertex, data->current_face_set); } if (!is_vertex_valid) { @@ -485,11 +494,11 @@ static bool pose_face_sets_floodfill_cb( /* Fallback origin accumulation. */ if (symmetry_check) { - add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, index)); + add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, vertex)); data->fallback_count++; } - if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, index)) { + if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, vertex)) { return visit_next; } @@ -498,15 +507,15 @@ static bool pose_face_sets_floodfill_cb( bool count_as_boundary = false; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.index); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.vertex); /* Check if we can get a valid face set for the next iteration from this neighbor. */ - if (SCULPT_vertex_has_unique_face_set(ss, ni.index) && + if (SCULPT_vertex_has_unique_face_set(ss, ni.vertex) && !BLI_gset_haskey(data->visited_face_sets, POINTER_FROM_INT(next_face_set_candidate))) { if (!data->next_face_set_found) { data->next_face_set = next_face_set_candidate; - data->next_vertex = ni.index; + data->next_vertex = ni.vertex; data->next_face_set_found = true; } count_as_boundary = true; @@ -516,7 +525,7 @@ static bool pose_face_sets_floodfill_cb( /* Origin accumulation. */ if (count_as_boundary) { - add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, index)); + add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, vertex)); data->tot_co++; } return visit_next; @@ -585,7 +594,7 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata, SculptVertexNeighborIter ni; float avg = 0.0f; int total = 0; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { avg += data->pose_factor[ni.index]; total++; } @@ -660,7 +669,8 @@ static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd, float next_chain_segment_target[3]; int totvert = SCULPT_vertex_count_get(ss); - int nearest_vertex_index = SCULPT_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true); + PBVHVertRef nearest_vertex = SCULPT_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true); + int nearest_vertex_index = BKE_pbvh_vertex_to_index(ss->pbvh, nearest_vertex); /* Init the buffers used to keep track of the changes in the pose factors as more segments are * added to the IK chain. */ @@ -745,7 +755,7 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets( int current_face_set = SCULPT_FACE_SET_NONE; int prev_face_set = SCULPT_FACE_SET_NONE; - int current_vertex = SCULPT_active_vertex_get(ss); + PBVHVertRef current_vertex = SCULPT_active_vertex_get(ss); for (int s = 0; s < ik_chain->tot_segments; s++) { @@ -801,15 +811,18 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets( } static bool pose_face_sets_fk_find_masked_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { PoseFloodFillData *data = 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); + if (!is_duplicate) { - data->floodfill_it[to_v] = data->floodfill_it[from_v] + 1; + data->floodfill_it[to_v_i] = data->floodfill_it[from_v_i] + 1; } else { - data->floodfill_it[to_v] = data->floodfill_it[from_v]; + data->floodfill_it[to_v_i] = data->floodfill_it[from_v_i]; } const int to_face_set = SCULPT_vertex_face_set_get(ss, to_v); @@ -820,9 +833,9 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb( BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(to_face_set)); - if (data->floodfill_it[to_v] >= data->masked_face_set_it) { + if (data->floodfill_it[to_v_i] >= data->masked_face_set_it) { data->masked_face_set = to_face_set; - data->masked_face_set_it = data->floodfill_it[to_v]; + data->masked_face_set_it = data->floodfill_it[to_v_i]; } if (data->target_face_set == SCULPT_FACE_SET_NONE) { @@ -834,11 +847,17 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb( return SCULPT_vertex_has_face_set(ss, to_v, data->initial_face_set); } -static bool pose_face_sets_fk_set_weights_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool UNUSED(is_duplicate), void *userdata) +static bool pose_face_sets_fk_set_weights_floodfill_cb(SculptSession *ss, + PBVHVertRef UNUSED(from_v), + PBVHVertRef to_v, + bool UNUSED(is_duplicate), + void *userdata) { PoseFloodFillData *data = userdata; - data->fk_weights[to_v] = 1.0f; + + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + + data->fk_weights[to_v_i] = 1.0f; return !SCULPT_vertex_has_face_set(ss, to_v, data->masked_face_set); } @@ -849,7 +868,9 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( SculptPoseIKChain *ik_chain = pose_ik_chain_new(1, totvert); - const int active_vertex = SCULPT_active_vertex_get(ss); + const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); + int active_vertex_index = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex); + const int active_face_set = SCULPT_active_face_set_get(ss); SculptFloodFill flood; @@ -857,7 +878,7 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( SCULPT_floodfill_add_initial(&flood, active_vertex); PoseFloodFillData fdata; fdata.floodfill_it = MEM_calloc_arrayN(totvert, sizeof(int), "floodfill iteration"); - fdata.floodfill_it[active_vertex] = 1; + fdata.floodfill_it[active_vertex_index] = 1; fdata.initial_face_set = active_face_set; fdata.masked_face_set = SCULPT_FACE_SET_NONE; fdata.target_face_set = SCULPT_FACE_SET_NONE; @@ -870,9 +891,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( int origin_count = 0; float origin_acc[3] = {0.0f}; for (int i = 0; i < totvert; i++) { - if (fdata.floodfill_it[i] != 0 && SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) && - SCULPT_vertex_has_face_set(ss, i, fdata.masked_face_set)) { - add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (fdata.floodfill_it[i] != 0 && + SCULPT_vertex_has_face_set(ss, vertex, fdata.initial_face_set) && + SCULPT_vertex_has_face_set(ss, vertex, fdata.masked_face_set)) { + add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, vertex)); origin_count++; } } @@ -881,10 +905,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( float target_acc[3] = {0.0f}; if (fdata.target_face_set != fdata.masked_face_set) { for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (fdata.floodfill_it[i] != 0 && - SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) && - SCULPT_vertex_has_face_set(ss, i, fdata.target_face_set)) { - add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, i)); + SCULPT_vertex_has_face_set(ss, vertex, fdata.initial_face_set) && + SCULPT_vertex_has_face_set(ss, vertex, fdata.target_face_set)) { + add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, vertex)); target_count++; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 53babc3d36d..2ef3c28ba0c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -46,26 +46,28 @@ #include <math.h> #include <stdlib.h> -void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index) +void SCULPT_neighbor_coords_average_interior(SculptSession *ss, + float result[3], + PBVHVertRef vertex) { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; int neighbor_count = 0; - const bool is_boundary = SCULPT_vertex_is_boundary(ss, index); + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { neighbor_count++; if (is_boundary) { /* Boundary vertices use only other boundary vertices. */ - if (SCULPT_vertex_is_boundary(ss, ni.index)) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } } else { /* Interior vertices use all neighbors. */ - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } } @@ -73,13 +75,13 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], /* Do not modify corner vertices. */ if (neighbor_count <= 2 && is_boundary) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); return; } /* Avoid division by 0 when there are no neighbors. */ if (total == 0) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); return; } @@ -134,14 +136,14 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert /* Generic functions for laplacian smoothing. These functions do not take boundary vertices into * account. */ -void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index) +void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], PBVHVertRef vertex) { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -150,18 +152,18 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int inde mul_v3_v3fl(result, avg, 1.0f / total); } else { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); } } -float SCULPT_neighbor_mask_average(SculptSession *ss, int index) +float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex) { float avg = 0.0f; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - avg += SCULPT_vertex_mask_get(ss, ni.index); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + avg += SCULPT_vertex_mask_get(ss, ni.vertex); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -169,19 +171,19 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index) if (total > 0) { return avg / total; } - return SCULPT_vertex_mask_get(ss, index); + return SCULPT_vertex_mask_get(ss, vertex); } -void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index) +void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef vertex) { float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f}; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { float tmp[4] = {0}; - SCULPT_vertex_color_get(ss, ni.index, tmp); + SCULPT_vertex_color_get(ss, ni.vertex, tmp); add_v4_v4(avg, tmp); total++; @@ -192,7 +194,7 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index mul_v4_v4fl(result, avg, 1.0f / total); } else { - SCULPT_vertex_color_get(ss, index, result); + SCULPT_vertex_color_get(ss, vertex, result); } } @@ -227,7 +229,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float disp[3]; @@ -235,7 +237,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, disp); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -258,9 +260,11 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, totvert, sizeof(float[3]), "details directions"); for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + SCULPT_neighbor_coords_average(ss, avg, vertex); + sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -309,23 +313,23 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), - vd.index, + vd.vertex, thread_id); if (smooth_mask) { - float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask; + float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask; val *= fade * bstrength; *vd.mask += val; CLAMP(*vd.mask, 0.0f, 1.0f); } else { float avg[3], val[3]; - SCULPT_neighbor_coords_average_interior(ss, avg, vd.index); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); - } - if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + if (vd.mvert) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } } } BKE_pbvh_vertex_iter_end; @@ -403,13 +407,15 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], float (*laplacian_disp)[3], - const int v_index, + const PBVHVertRef vertex, const float origco[3], const float alpha) { float laplacian_smooth_co[3]; float weigthed_o[3], weigthed_q[3], d[3]; - SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index); + int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + + SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex); mul_v3_v3fl(weigthed_o, origco, alpha); mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); @@ -422,7 +428,7 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, void SCULPT_surface_smooth_displace_step(SculptSession *ss, float *co, float (*laplacian_disp)[3], - const int v_index, + const PBVHVertRef vertex, const float beta, const float fade) { @@ -430,12 +436,15 @@ void SCULPT_surface_smooth_displace_step(SculptSession *ss, float b_current_vertex[3]; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { add_v3_v3(b_avg, laplacian_disp[ni.index]); total++; } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); if (total > 0) { + int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / total); madd_v3_v3fl(b_current_vertex, laplacian_disp[v_index], beta); mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f)); @@ -460,7 +469,7 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); @@ -474,15 +483,15 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float disp[3]; SCULPT_surface_smooth_laplacian_step( - ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, orig_data.co, alpha); + ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, orig_data.co, alpha); madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -515,10 +524,10 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); SCULPT_surface_smooth_displace_step( - ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade); + ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade); } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 48033f3407e..dfaa0bd4daa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -46,7 +46,7 @@ #include <math.h> #include <stdlib.h> -void ED_sculpt_init_transform(struct bContext *C, Object *ob) +void ED_sculpt_init_transform(struct bContext *C, Object *ob, const char *undo_name) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; SculptSession *ss = ob->sculpt; @@ -60,7 +60,7 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob) copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot); copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale); - SCULPT_undo_push_begin(ob, "Transform"); + SCULPT_undo_push_begin_ex(ob, undo_name); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); ss->pivot_rot[3] = 1.0f; @@ -150,7 +150,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata, PBVHNode *node = data->nodes[i]; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); PBVHVertexIter vd; @@ -179,7 +179,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata, add_v3_v3v3(vd.co, start_co, disp); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -221,7 +221,7 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata, float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[i])->co; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); KelvinletParams params; /* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume @@ -253,7 +253,7 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata, copy_v3_v3(proxy[vd.i], final_disp); if (vd.mvert) { - BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -289,9 +289,6 @@ static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float flip_v3_v3(data.elastic_transform_pivot, ss->pivot_pos, symmpass); flip_v3_v3(data.elastic_transform_pivot_init, ss->init_pivot_pos, symmpass); - printf( - "%.2f %.2f %.2f\n", ss->init_pivot_pos[0], ss->init_pivot_pos[1], ss->init_pivot_pos[2]); - const int symm_area = SCULPT_get_vertex_symm_area(data.elastic_transform_pivot); copy_m4_m4(data.elastic_transform_mat, data.transform_mats[symm_area]); BLI_task_parallel_range( @@ -354,11 +351,6 @@ void ED_sculpt_end_transform(struct bContext *C, Object *ob) if (ss->filter_cache) { SCULPT_filter_cache_free(ss); } - /* Force undo push to happen even inside transform operator, since the sculpt - * undo system works separate from regular undo and this is require to properly - * finish an undo step also when canceling. */ - const bool use_nested_undo = true; - SCULPT_undo_push_end_ex(ob, use_nested_undo); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); } @@ -426,7 +418,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) RNA_float_get(op->ptr, "mouse_x"), RNA_float_get(op->ptr, "mouse_y"), }; - if (SCULPT_stroke_get_location(C, stroke_location, mval)) { + if (SCULPT_stroke_get_location(C, stroke_location, mval, false)) { copy_v3_v3(ss->pivot_pos, stroke_location); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index e82f14b1ca7..a31be07d8af 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -4,6 +4,29 @@ /** \file * \ingroup edsculpt * Implements the Sculpt Mode tools. + * + * Usage Guide + * =========== + * + * The sculpt undo system is a delta-based system. Each undo step stores + * the difference with the prior one. + * + * To use the sculpt undo system, you must call SCULPT_undo_push_begin + * inside an operator exec or invoke callback (ED_sculpt_undo_geometry_begin + * may be called if you wish to save a non-delta copy of the entire mesh). + * This will initialize the sculpt undo stack and set up an undo step. + * + * At the end of the operator you should call SCULPT_undo_push_end. + * + * SCULPT_undo_push_end and ED_sculpt_undo_geometry_begin both take a + * #wmOperatorType as an argument. There are _ex versions that allow a custom + * name; try to avoid using them. These can break the redo panel since it requires + * the undo push have the same name as the calling operator. + * + * NOTE: Sculpt undo steps are not appended to the global undo stack until + * the operator finishes. We use BKE_undosys_step_push_init_with_type to build + * a tentative undo step with is appended later when the operator ends. + * Operators must have the OPTYPE_UNDO flag set for this to work properly. */ #include <stddef.h> @@ -30,6 +53,7 @@ #include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_key.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" @@ -144,6 +168,9 @@ struct PartialUpdateData { PBVH *pbvh; bool rebuild; char *modified_grids; + bool *modified_hidden_verts; + bool *modified_mask_verts; + bool *modified_color_verts; }; /** @@ -167,8 +194,39 @@ static void update_cb_partial(PBVHNode *node, void *userdata) } } else { - if (BKE_pbvh_node_vert_update_check_any(data->pbvh, node)) { - update_cb(node, &(data->rebuild)); + if (BKE_pbvh_node_has_vert_with_normal_update_tag(data->pbvh, node)) { + BKE_pbvh_node_mark_update(node); + } + int verts_num; + const int *vert_indices; + BKE_pbvh_node_num_verts(data->pbvh, node, NULL, &verts_num); + BKE_pbvh_node_get_verts(data->pbvh, node, &vert_indices, NULL); + if (data->modified_mask_verts != NULL) { + for (int i = 0; i < verts_num; i++) { + if (data->modified_mask_verts[vert_indices[i]]) { + BKE_pbvh_node_mark_update_mask(node); + break; + } + } + } + if (data->modified_color_verts != NULL) { + for (int i = 0; i < verts_num; i++) { + if (data->modified_color_verts[vert_indices[i]]) { + BKE_pbvh_node_mark_update_color(node); + break; + } + } + } + if (data->modified_hidden_verts != NULL) { + for (int i = 0; i < verts_num; i++) { + if (data->modified_hidden_verts[vert_indices[i]]) { + if (data->rebuild) { + BKE_pbvh_node_mark_update_visibility(node); + } + BKE_pbvh_node_fully_hidden_set(node, 0); + break; + } + } } } } @@ -196,7 +254,7 @@ static bool sculpt_undo_restore_deformed( static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, SculptUndoNode *unode) { ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; MVert *mvert; @@ -263,20 +321,20 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt if (ss->deform_modifiers_active) { for (int i = 0; i < unode->totvert; i++) { sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co); - BKE_pbvh_vert_mark_update(ss->pbvh, index[i]); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i])); } } else { for (int i = 0; i < unode->totvert; i++) { swap_v3_v3(mvert[index[i]].co, unode->orig_co[i]); - BKE_pbvh_vert_mark_update(ss->pbvh, index[i]); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i])); } } } else { for (int i = 0; i < unode->totvert; i++) { swap_v3_v3(mvert[index[i]].co, unode->co[i]); - BKE_pbvh_vert_mark_update(ss->pbvh, index[i]); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i])); } } } @@ -305,22 +363,22 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt return true; } -static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode) +static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool *modified_vertices) { ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; - if (unode->maxvert) { - MVert *mvert = ss->mvert; + bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh); + if (unode->maxvert) { for (int i = 0; i < unode->totvert; i++) { - MVert *v = &mvert[unode->index[i]]; - if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) { + const int vert_index = unode->index[i]; + if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != hide_vert[vert_index]) { BLI_BITMAP_FLIP(unode->vert_hidden, i); - v->flag ^= ME_HIDE; - BKE_pbvh_vert_mark_update(ss->pbvh, unode->index[i]); + hide_vert[vert_index] = !hide_vert[vert_index]; + modified_vertices[vert_index] = true; } } } @@ -335,10 +393,10 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode) return true; } -static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode) +static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode, bool *modified_vertices) { ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; bool modified = false; @@ -360,17 +418,17 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode) if (modified) { for (int i = 0; i < unode->totvert; i++) { - BKE_pbvh_vert_mark_update(ss->pbvh, unode->index[i]); + modified_vertices[unode->index[i]] = true; } } return modified; } -static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode) +static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode, bool *modified_vertices) { ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; float *vmask; @@ -385,7 +443,7 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode) for (int i = 0; i < unode->totvert; i++) { if (vmask[index[i]] != unode->mask[i]) { SWAP(float, vmask[index[i]], unode->mask[i]); - BKE_pbvh_vert_mark_update(ss->pbvh, index[i]); + modified_vertices[index[i]] = true; } } } @@ -416,7 +474,7 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode) static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode) { ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); Mesh *me = BKE_object_get_original_mesh(ob); int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); for (int i = 0; i < me->totpoly; i++) { @@ -569,8 +627,6 @@ static void sculpt_undo_geometry_restore_data(SculptUndoNodeGeometry *geometry, CustomData_copy( &geometry->pdata, &mesh->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly); - BKE_mesh_update_customdata_pointers(mesh, false); - BKE_mesh_runtime_clear_cache(mesh); } @@ -665,7 +721,7 @@ 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); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; SculptUndoNode *unode; @@ -699,7 +755,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_vertices(ob); + SCULPT_visibility_sync_all_face_sets_to_verts(ob); BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); @@ -731,6 +787,12 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } + /* The PBVH already keeps track of which vertices need updated normals, but it doesn't keep track + * of other updated. In order to tell the corresponding PBVH nodes to update, keep track of which + * elements were updated for specific layers. */ + bool *modified_hidden_verts = NULL; + bool *modified_mask_verts = NULL; + bool *modified_color_verts = NULL; char *undo_modified_grids = NULL; bool use_multires_undo = false; @@ -763,13 +825,19 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } break; case SCULPT_UNDO_HIDDEN: - if (sculpt_undo_restore_hidden(C, unode)) { + if (modified_hidden_verts == NULL) { + modified_hidden_verts = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__); + } + if (sculpt_undo_restore_hidden(C, unode, modified_hidden_verts)) { rebuild = true; update_visibility = true; } break; case SCULPT_UNDO_MASK: - if (sculpt_undo_restore_mask(C, unode)) { + if (modified_mask_verts == NULL) { + modified_mask_verts = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__); + } + if (sculpt_undo_restore_mask(C, unode, modified_mask_verts)) { update = true; update_mask = true; } @@ -777,7 +845,10 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase case SCULPT_UNDO_FACE_SETS: break; case SCULPT_UNDO_COLOR: - if (sculpt_undo_restore_color(C, unode)) { + if (modified_color_verts == NULL) { + modified_color_verts = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__); + } + if (sculpt_undo_restore_color(C, unode, modified_color_verts)) { update = true; } @@ -828,6 +899,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase .rebuild = rebuild, .pbvh = ss->pbvh, .modified_grids = undo_modified_grids, + .modified_hidden_verts = modified_hidden_verts, + .modified_mask_verts = modified_mask_verts, + .modified_color_verts = modified_color_verts, }; BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); @@ -855,7 +929,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase if (tag_update) { Mesh *mesh = ob->data; - BKE_mesh_normals_tag_dirty(mesh); + BKE_mesh_tag_coords_changed(mesh); BKE_sculptsession_free_deformMats(ss); } @@ -873,6 +947,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } + MEM_SAFE_FREE(modified_hidden_verts); + MEM_SAFE_FREE(modified_mask_verts); + MEM_SAFE_FREE(modified_color_verts); MEM_SAFE_FREE(undo_modified_grids); } @@ -944,7 +1021,7 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); SculptUndoNode *unode; unode = lb->first; @@ -961,7 +1038,7 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) } #endif -SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node) +SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); @@ -969,7 +1046,13 @@ SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node) return NULL; } - return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node)); + LISTBASE_FOREACH (SculptUndoNode *, unode, &usculpt->nodes) { + if (unode->node == node && unode->type == type) { + return unode; + } + } + + return NULL; } SculptUndoNode *SCULPT_undo_get_first_node() @@ -1081,8 +1164,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt unode->co = MEM_callocN(alloc_size, "SculptUndoNode.co"); usculpt->undo_size += alloc_size; - /* FIXME: Should explain why this is allocated here, to be freed in - * `SCULPT_undo_push_end_ex()`? */ + /* Needed for original data lookup. */ alloc_size = sizeof(*unode->no) * (size_t)allvert; unode->no = MEM_callocN(alloc_size, "SculptUndoNode.no"); usculpt->undo_size += alloc_size; @@ -1185,6 +1267,11 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) PBVH *pbvh = ob->sculpt->pbvh; PBVHNode *node = unode->node; + const bool *hide_vert = BKE_pbvh_get_vert_hide(pbvh); + if (hide_vert == NULL) { + return; + } + if (unode->grids) { /* Already stored during allocation. */ } @@ -1196,7 +1283,7 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert); BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); for (int i = 0; i < allvert; i++) { - BLI_BITMAP_SET(unode->vert_hidden, i, mvert[vert_indices[i]].flag & ME_HIDE); + BLI_BITMAP_SET(unode->vert_hidden, i, hide_vert[vert_indices[i]]); } } } @@ -1381,7 +1468,7 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType BLI_thread_unlock(LOCK_CUSTOM1); return unode; } - if ((unode = SCULPT_undo_get_node(node))) { + if ((unode = SCULPT_undo_get_node(node, type))) { BLI_thread_unlock(LOCK_CUSTOM1); return unode; } @@ -1480,7 +1567,12 @@ static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr) attr->was_set = true; } -void SCULPT_undo_push_begin(Object *ob, const char *name) +void SCULPT_undo_push_begin(Object *ob, const wmOperator *op) +{ + SCULPT_undo_push_begin_ex(ob, op->type->name); +} + +void SCULPT_undo_push_begin_ex(Object *ob, const char *name) { UndoStack *ustack = ED_undo_stack_get(); @@ -1576,11 +1668,12 @@ static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr */ if (!layer) { layer = BKE_id_attribute_search(&me->id, attr->name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); - eAttrDomain domain = layer ? BKE_id_attribute_domain(&me->id, layer) : ATTR_DOMAIN_NUM; - - if (layer && ED_geometry_attribute_convert( - me, attr->name, layer->type, domain, attr->type, attr->domain)) { - layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); + if (layer) { + const eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer); + if (ED_geometry_attribute_convert( + me, attr->name, layer->type, domain, attr->type, attr->domain)) { + layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); + } } } @@ -1589,7 +1682,7 @@ static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &me->vdata : &me->ldata; int totelem = attr->domain == ATTR_DOMAIN_POINT ? me->totvert : me->totloop; - CustomData_add_layer_named(cdata, attr->type, CD_DEFAULT, NULL, totelem, attr->name); + CustomData_add_layer_named(cdata, attr->type, CD_SET_DEFAULT, NULL, totelem, attr->name); layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); } @@ -1722,7 +1815,7 @@ static void sculpt_undosys_step_decode( { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(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)) { /* Pass. */ @@ -1768,9 +1861,15 @@ static void sculpt_undosys_step_free(UndoStep *us_p) sculpt_undo_free_list(&us->data.nodes); } -void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name) +void ED_sculpt_undo_geometry_begin(struct Object *ob, const wmOperator *op) +{ + SCULPT_undo_push_begin(ob, op); + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY); +} + +void ED_sculpt_undo_geometry_begin_ex(struct Object *ob, const char *name) { - SCULPT_undo_push_begin(ob, name); + SCULPT_undo_push_begin_ex(ob, name); SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY); } @@ -1883,7 +1982,7 @@ void ED_sculpt_undo_push_multires_mesh_begin(bContext *C, const char *str) Object *object = CTX_data_active_object(C); - SCULPT_undo_push_begin(object, str); + SCULPT_undo_push_begin_ex(object, str); SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY); geometry_unode->geometry_clear_pbvh = false; diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 8e1f4f4d495..8b9776cf94d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -9,7 +9,7 @@ #include "MEM_guardedalloc.h" #include "BLI_ghash.h" -#include "BLI_math.h" +#include "BLI_math_base_safe.h" #include "BLI_utildefines.h" #include "DNA_brush_types.h" @@ -22,6 +22,7 @@ #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" +#include "BKE_image.h" #include "BKE_mesh_mapping.h" #include "BKE_paint.h" @@ -30,6 +31,7 @@ #include "ED_image.h" #include "ED_mesh.h" #include "ED_screen.h" +#include "ED_uvedit.h" #include "WM_api.h" #include "WM_types.h" @@ -42,6 +44,9 @@ #include "UI_view2d.h" +/* When set, the UV element is on the boundary of the graph. + * i.e. Instead of a 2-dimensional laplace operator, use a 1-dimensional version. + * Visually, UV elements on the graph boundary appear as borders of the UV Island. */ #define MARK_BOUNDARY 1 typedef struct UvAdjacencyElement { @@ -49,16 +54,17 @@ typedef struct UvAdjacencyElement { UvElement *element; /* uv pointer for convenience. Caution, this points to the original UVs! */ float *uv; - /* general use flag (Used to check if Element is boundary here) */ - char flag; + /* Are we on locked in place? */ + bool is_locked; + /* Are we on the boundary? */ + bool is_boundary; } UvAdjacencyElement; typedef struct UvEdge { uint uv1; uint uv2; - /* general use flag - * (Used to check if edge is boundary here, and propagates to adjacency elements) */ - char flag; + /* Are we in the interior? */ + bool is_interior; } UvEdge; typedef struct UVInitialStrokeElement { @@ -90,13 +96,13 @@ typedef struct UvSculptData { * to their coincident UV's */ UvAdjacencyElement *uv; - /* ...Is what it says */ + /* Total number of unique UVs. */ int totalUniqueUvs; /* Edges used for adjacency info, used with laplacian smoothing */ UvEdge *uvedges; - /* need I say more? */ + /* Total number of #UvEdge. */ int totalUvEdges; /* data for initial stroke, used by tools like grab */ @@ -116,8 +122,25 @@ typedef struct UvSculptData { /* store invert flag here */ char invert; + + /* Is constrain to image bounds active? */ + bool constrain_to_bounds; + + /* Base for constrain_to_bounds. */ + float uv_base_offset[2]; } UvSculptData; +static void apply_sculpt_data_constraints(UvSculptData *sculptdata, float uv[2]) +{ + if (!sculptdata->constrain_to_bounds) { + return; + } + float u = sculptdata->uv_base_offset[0]; + float v = sculptdata->uv_base_offset[1]; + uv[0] = clamp_f(uv[0], u, u + 1.0f); + uv[1] = clamp_f(uv[1], v, v + 1.0f); +} + /*********** Improved Laplacian Relaxation Operator ************************/ /* original code by Raul Fernandez Hernandez "farsthary" * * adapted to uv smoothing by Antony Riakiatakis * @@ -170,17 +193,14 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em, } for (i = 0; i < sculptdata->totalUniqueUvs; i++) { - float dist; - /* This is supposed to happen only if "Pin Edges" is on, - * since we have initialization on stroke start. - * If ever uv brushes get their own mode we should check for toolsettings option too. */ - if (sculptdata->uv[i].flag & MARK_BOUNDARY) { + if (sculptdata->uv[i].is_locked) { continue; } sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); diff[1] /= aspectRatio; - if ((dist = dot_v2v2(diff, diff)) <= radius) { + float dist = dot_v2v2(diff, diff); + if (dist <= radius) { UvElement *element; float strength; strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); @@ -196,6 +216,8 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em, 0.5f * (tmp_uvdata[i].b[1] + tmp_uvdata[i].sum_b[1] / tmp_uvdata[i].ncounter)); + apply_sculpt_data_constraints(sculptdata, sculptdata->uv[i].uv); + for (element = sculptdata->uv[i].element; element; element = element->next) { MLoopUV *luv; BMLoop *l; @@ -211,9 +233,16 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em, } } - MEM_freeN(tmp_uvdata); + MEM_SAFE_FREE(tmp_uvdata); } +/* Legacy version which only does laplacian relaxation. + * Probably a little faster as it caches UvEdges. + * Mostly preserved for comparison with `HC_relaxation_iteration_uv`. + * Once the HC method has been merged into `relaxation_iteration_uv`, + * all the `HC_*` and `laplacian_*` specific functions can probably be removed. + */ + static void laplacian_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, const float mouse_coord[2], @@ -233,14 +262,19 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, /* counting neighbors */ for (i = 0; i < sculptdata->totalUvEdges; i++) { UvEdge *tmpedge = sculptdata->uvedges + i; - tmp_uvdata[tmpedge->uv1].ncounter++; - tmp_uvdata[tmpedge->uv2].ncounter++; - - add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); - add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); + bool code1 = sculptdata->uv[sculptdata->uvedges[i].uv1].is_boundary; + bool code2 = sculptdata->uv[sculptdata->uvedges[i].uv2].is_boundary; + if (code1 || (code1 == code2)) { + tmp_uvdata[tmpedge->uv2].ncounter++; + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); + } + if (code2 || (code1 == code2)) { + tmp_uvdata[tmpedge->uv1].ncounter++; + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); + } } - /* Original Lacplacian algorithm included removal of normal component of translation. + /* Original Laplacian algorithm included removal of normal component of translation. * here it is not needed since we translate along the UV plane always. */ for (i = 0; i < sculptdata->totalUniqueUvs; i++) { copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co); @@ -248,17 +282,14 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, } for (i = 0; i < sculptdata->totalUniqueUvs; i++) { - float dist; - /* This is supposed to happen only if "Pin Edges" is on, - * since we have initialization on stroke start. - * If ever uv brushes get their own mode we should check for toolsettings option too. */ - if (sculptdata->uv[i].flag & MARK_BOUNDARY) { + if (sculptdata->uv[i].is_locked) { continue; } sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); diff[1] /= aspectRatio; - if ((dist = dot_v2v2(diff, diff)) <= radius) { + float dist = dot_v2v2(diff, diff); + if (dist <= radius) { UvElement *element; float strength; strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); @@ -268,6 +299,8 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] + strength * tmp_uvdata[i].p[1]; + apply_sculpt_data_constraints(sculptdata, sculptdata->uv[i].uv); + for (element = sculptdata->uv[i].element; element; element = element->next) { MLoopUV *luv; BMLoop *l; @@ -283,7 +316,154 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, } } - MEM_freeN(tmp_uvdata); + MEM_SAFE_FREE(tmp_uvdata); +} + +static void add_weighted_edge(float (*delta_buf)[3], + const UvElement *storage, + const UvElement *ele_next, + const UvElement *ele_prev, + const MLoopUV *luv_next, + const MLoopUV *luv_prev, + const float weight) +{ + float delta[2]; + sub_v2_v2v2(delta, luv_next->uv, luv_prev->uv); + + bool code1 = (ele_prev->flag & MARK_BOUNDARY); + bool code2 = (ele_next->flag & MARK_BOUNDARY); + if (code1 || (code1 == code2)) { + int index_next = ele_next - storage; + delta_buf[index_next][0] -= delta[0] * weight; + delta_buf[index_next][1] -= delta[1] * weight; + delta_buf[index_next][2] += fabsf(weight); + } + if (code2 || (code1 == code2)) { + int index_prev = ele_prev - storage; + delta_buf[index_prev][0] += delta[0] * weight; + delta_buf[index_prev][1] += delta[1] * weight; + delta_buf[index_prev][2] += fabsf(weight); + } +} + +static float tri_weight_v3(int method, const float *v1, const float *v2, const float *v3) +{ + switch (method) { + case UV_SCULPT_TOOL_RELAX_LAPLACIAN: + case UV_SCULPT_TOOL_RELAX_HC: + return 1.0f; + case UV_SCULPT_TOOL_RELAX_COTAN: + return cotangent_tri_weight_v3(v1, v2, v3); + default: + BLI_assert_unreachable(); + } + return 0.0f; +} + +static void relaxation_iteration_uv(BMEditMesh *em, + UvSculptData *sculptdata, + const float mouse_coord[2], + const float alpha, + const float radius_squared, + const float aspect_ratio, + const int method) +{ + if (method == UV_SCULPT_TOOL_RELAX_HC) { + HC_relaxation_iteration_uv(em, sculptdata, mouse_coord, alpha, radius_squared, aspect_ratio); + return; + } + if (method == UV_SCULPT_TOOL_RELAX_LAPLACIAN) { + laplacian_relaxation_iteration_uv( + em, sculptdata, mouse_coord, alpha, radius_squared, aspect_ratio); + return; + } + + struct UvElement **head_table = BM_uv_element_map_ensure_head_table(sculptdata->elementMap); + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + BLI_assert(cd_loop_uv_offset >= 0); + + const int total_uvs = sculptdata->elementMap->total_uvs; + float(*delta_buf)[3] = (float(*)[3])MEM_callocN(total_uvs * sizeof(float[3]), __func__); + + const UvElement *storage = sculptdata->elementMap->storage; + for (int j = 0; j < total_uvs; j++) { + const UvElement *ele_curr = storage + j; + const BMFace *efa = ele_curr->l->f; + const UvElement *ele_next = BM_uv_element_get(sculptdata->elementMap, efa, ele_curr->l->next); + const UvElement *ele_prev = BM_uv_element_get(sculptdata->elementMap, efa, ele_curr->l->prev); + + const float *v_curr_co = ele_curr->l->v->co; + const float *v_prev_co = ele_prev->l->v->co; + const float *v_next_co = ele_next->l->v->co; + + const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(ele_curr->l, cd_loop_uv_offset); + const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(ele_next->l, cd_loop_uv_offset); + const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(ele_prev->l, cd_loop_uv_offset); + + const UvElement *head_curr = head_table[ele_curr - sculptdata->elementMap->storage]; + const UvElement *head_next = head_table[ele_next - sculptdata->elementMap->storage]; + const UvElement *head_prev = head_table[ele_prev - sculptdata->elementMap->storage]; + + /* If the mesh is triangulated with no boundaries, only one edge is required. */ + const float weight_curr = tri_weight_v3(method, v_curr_co, v_prev_co, v_next_co); + add_weighted_edge(delta_buf, storage, head_next, head_prev, luv_next, luv_prev, weight_curr); + + /* Triangulated with a boundary? We need the incoming edges to solve the boundary. */ + const float weight_prev = tri_weight_v3(method, v_prev_co, v_curr_co, v_next_co); + add_weighted_edge(delta_buf, storage, head_next, head_curr, luv_next, luv_curr, weight_prev); + + if (method == UV_SCULPT_TOOL_RELAX_LAPLACIAN) { + /* Laplacian method has zero weights on virtual edges. */ + continue; + } + + /* Meshes with quads (or other n-gons) need "virtual" edges too. */ + const float weight_next = tri_weight_v3(method, v_next_co, v_curr_co, v_prev_co); + add_weighted_edge(delta_buf, storage, head_prev, head_curr, luv_prev, luv_curr, weight_next); + } + + Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); + for (int i = 0; i < sculptdata->totalUniqueUvs; i++) { + UvAdjacencyElement *adj_el = &sculptdata->uv[i]; + if (adj_el->is_locked) { + continue; /* Locked UVs can't move. */ + } + + /* Is UV within brush's influence? */ + float diff[2]; + sub_v2_v2v2(diff, adj_el->uv, mouse_coord); + diff[1] /= aspect_ratio; + const float dist_squared = len_squared_v2(diff); + if (dist_squared > radius_squared) { + continue; + } + const float strength = alpha * BKE_brush_curve_strength_clamped( + brush, sqrtf(dist_squared), sqrtf(radius_squared)); + + const float *delta_sum = delta_buf[adj_el->element - storage]; + + { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(adj_el->element->l, cd_loop_uv_offset); + BLI_assert(adj_el->uv == luv->uv); /* Only true for head. */ + adj_el->uv[0] = luv->uv[0] + strength * safe_divide(delta_sum[0], delta_sum[2]); + adj_el->uv[1] = luv->uv[1] + strength * safe_divide(delta_sum[1], delta_sum[2]); + apply_sculpt_data_constraints(sculptdata, adj_el->uv); + } + + /* Copy UV co-ordinates to all UvElements. */ + UvElement *tail = adj_el->element; + while (tail) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(tail->l, cd_loop_uv_offset); + copy_v2_v2(luv->uv, adj_el->uv); + tail = tail->next; + if (tail && tail->separate) { + break; + } + } + } + + MEM_SAFE_FREE(delta_buf); } static void uv_sculpt_stroke_apply(bContext *C, @@ -327,17 +507,15 @@ static void uv_sculpt_stroke_apply(bContext *C, int i; alpha *= invert; for (i = 0; i < sculptdata->totalUniqueUvs; i++) { - float dist, diff[2]; - /* This is supposed to happen only if "Lock Borders" is on, - * since we have initialization on stroke start. - * If ever uv brushes get their own mode we should check for toolsettings option too. */ - if (sculptdata->uv[i].flag & MARK_BOUNDARY) { + if (sculptdata->uv[i].is_locked) { continue; } + float diff[2]; sub_v2_v2v2(diff, sculptdata->uv[i].uv, co); diff[1] /= aspectRatio; - if ((dist = dot_v2v2(diff, diff)) <= radius) { + float dist = dot_v2v2(diff, diff); + if (dist <= radius) { UvElement *element; float strength; strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); @@ -346,6 +524,8 @@ static void uv_sculpt_stroke_apply(bContext *C, sculptdata->uv[i].uv[0] -= strength * diff[0] * 0.001f; sculptdata->uv[i].uv[1] -= strength * diff[1] * 0.001f; + apply_sculpt_data_constraints(sculptdata, sculptdata->uv[i].uv); + for (element = sculptdata->uv[i].element; element; element = element->next) { MLoopUV *luv; BMLoop *l; @@ -363,16 +543,11 @@ static void uv_sculpt_stroke_apply(bContext *C, } /* - * Smooth Tool + * Relax Tool */ else if (tool == UV_SCULPT_TOOL_RELAX) { - uint method = toolsettings->uv_relax_method; - if (method == UV_SCULPT_TOOL_RELAX_HC) { - HC_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); - } - else { - laplacian_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); - } + relaxation_iteration_uv( + em, sculptdata, co, alpha, radius, aspectRatio, toolsettings->uv_relax_method); } /* @@ -392,6 +567,8 @@ static void uv_sculpt_stroke_apply(bContext *C, sculptdata->uv[uvindex].uv[1] = sculptdata->initial_stroke->initialSelection[i].initial_uv[1] + strength * diff[1]; + apply_sculpt_data_constraints(sculptdata, sculptdata->uv[uvindex].uv); + for (element = sculptdata->uv[uvindex].element; element; element = element->next) { MLoopUV *luv; BMLoop *l; @@ -405,32 +582,32 @@ static void uv_sculpt_stroke_apply(bContext *C, copy_v2_v2(luv->uv, sculptdata->uv[uvindex].uv); } } + if (sima->flag & SI_LIVE_UNWRAP) { + ED_uvedit_live_unwrap_re_solve(); + } } } static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op) { + SpaceImage *sima = CTX_wm_space_image(C); + if (sima->flag & SI_LIVE_UNWRAP) { + ED_uvedit_live_unwrap_end(false); + } UvSculptData *data = op->customdata; if (data->timer) { WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), data->timer); } - if (data->elementMap) { - BM_uv_element_map_free(data->elementMap); - } - if (data->uv) { - MEM_freeN(data->uv); - } - if (data->uvedges) { - MEM_freeN(data->uvedges); - } + BM_uv_element_map_free(data->elementMap); + data->elementMap = NULL; + MEM_SAFE_FREE(data->uv); + MEM_SAFE_FREE(data->uvedges); if (data->initial_stroke) { - if (data->initial_stroke->initialSelection) { - MEM_freeN(data->initial_stroke->initialSelection); - } - MEM_freeN(data->initial_stroke); + MEM_SAFE_FREE(data->initial_stroke->initialSelection); + MEM_SAFE_FREE(data->initial_stroke); } - MEM_freeN(data); + MEM_SAFE_FREE(data); op->customdata = NULL; } @@ -441,7 +618,7 @@ static int uv_element_offset_from_face_get( if (!element || (doIslands && element->island != island_index)) { return -1; } - return element - map->buf; + return element - map->storage; } static uint uv_edge_hash(const void *key) @@ -461,6 +638,17 @@ static bool uv_edge_compare(const void *a, const void *b) return true; } +static void set_element_flag(UvElement *element, const int flag) +{ + while (element) { + element->flag |= flag; + element = element->next; + if (!element || element->separate) { + break; + } + } +} + static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wmEvent *event) { Scene *scene = CTX_data_scene(C); @@ -475,7 +663,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm BKE_curvemapping_init(ts->uvsculpt->paint.brush->curve); if (data) { - int counter = 0, i; ARegion *region = CTX_wm_region(C); float co[2]; BMFace *efa; @@ -489,8 +676,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS); int island_index = 0; - /* Holds, for each UvElement in elementMap, a pointer to its unique UV. */ - int *uniqueUv; data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ? UV_SCULPT_TOOL_RELAX : ts->uvsculpt->paint.brush->uv_sculpt_tool; @@ -498,23 +683,12 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm data->uvsculpt = &ts->uvsculpt->paint; - if (do_island_optimization) { - /* We will need island information */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, true); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, true); - } - } - else { - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, false); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, false); - } - } + /* Winding was added to island detection in 5197aa04c6bd + * However the sculpt tools can flip faces, potentially creating orphaned islands. + * See T100132 */ + bool use_winding = false; + data->elementMap = BM_uv_element_map_create( + bm, scene, false, use_winding, do_island_optimization); if (!data->elementMap) { uv_sculpt_stroke_exit(C, op); @@ -535,27 +709,22 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm } /* Count 'unique' UV's */ - for (i = 0; i < data->elementMap->totalUVs; i++) { - if (data->elementMap->buf[i].separate && - (!do_island_optimization || data->elementMap->buf[i].island == island_index)) { - counter++; - } + int unique_uvs = data->elementMap->total_unique_uvs; + if (do_island_optimization) { + unique_uvs = data->elementMap->island_total_unique_uvs[island_index]; } /* Allocate the unique uv buffers */ - data->uv = MEM_mallocN(sizeof(*data->uv) * counter, "uv_brush_unique_uvs"); - uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->totalUVs, - "uv_brush_unique_uv_map"); + data->uv = MEM_callocN(sizeof(*data->uv) * unique_uvs, "uv_brush_unique_uvs"); + /* Holds, for each UvElement in elementMap, an index of its unique UV. */ + int *uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->total_uvs, + "uv_brush_unique_uv_map"); edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash"); /* we have at most totalUVs edges */ - edges = MEM_mallocN(sizeof(*edges) * data->elementMap->totalUVs, "uv_brush_all_edges"); + edges = MEM_callocN(sizeof(*edges) * data->elementMap->total_uvs, "uv_brush_all_edges"); if (!data->uv || !uniqueUv || !edgeHash || !edges) { - if (edges) { - MEM_freeN(edges); - } - if (uniqueUv) { - MEM_freeN(uniqueUv); - } + MEM_SAFE_FREE(edges); + MEM_SAFE_FREE(uniqueUv); if (edgeHash) { BLI_ghash_free(edgeHash, NULL, NULL); } @@ -563,12 +732,12 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm return NULL; } - data->totalUniqueUvs = counter; - /* So that we can use this as index for the UvElements */ - counter = -1; + data->totalUniqueUvs = unique_uvs; + /* Index for the UvElements. */ + int counter = -1; /* initialize the unique UVs */ - for (i = 0; i < bm->totvert; i++) { - UvElement *element = data->elementMap->vert[i]; + for (int i = 0; i < bm->totvert; i++) { + UvElement *element = data->elementMap->vertex[i]; for (; element; element = element->next) { if (element->separate) { if (do_island_optimization && (element->island != island_index)) { @@ -584,13 +753,18 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm counter++; data->uv[counter].element = element; - data->uv[counter].flag = 0; data->uv[counter].uv = luv->uv; + if (data->tool != UV_SCULPT_TOOL_GRAB) { + if (luv->flag & MLOOPUV_PINNED) { + data->uv[counter].is_locked = true; + } + } } /* Pointer arithmetic to the rescue, as always :). */ - uniqueUv[element - data->elementMap->buf] = counter; + uniqueUv[element - data->elementMap->storage] = counter; } } + BLI_assert(counter + 1 == unique_uvs); /* Now, on to generate our uv connectivity data */ counter = 0; @@ -600,7 +774,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm data->elementMap, efa, l, island_index, do_island_optimization); int offset2, itmp2 = uv_element_offset_from_face_get( data->elementMap, efa, l->next, island_index, do_island_optimization); - char *flag; /* Skip edge if not found(unlikely) or not on valid island */ if (itmp1 == -1 || itmp2 == -1) { @@ -610,7 +783,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm offset1 = uniqueUv[itmp1]; offset2 = uniqueUv[itmp2]; - edges[counter].flag = 0; /* Using an order policy, sort UV's according to address space. * This avoids having two different UvEdges with the same UV's on different positions. */ if (offset1 < offset2) { @@ -621,58 +793,65 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm edges[counter].uv1 = offset2; edges[counter].uv2 = offset1; } - /* Hack! Set the value of the key to its flag. - * Now we can set the flag when an edge exists twice :) */ - flag = BLI_ghash_lookup(edgeHash, &edges[counter]); - if (flag) { - *flag = 1; + UvEdge *prev_edge = BLI_ghash_lookup(edgeHash, &edges[counter]); + if (prev_edge) { + prev_edge->is_interior = true; + edges[counter].is_interior = true; } else { - /* Hack mentioned */ - BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter].flag); + BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter]); } counter++; } } - MEM_freeN(uniqueUv); + MEM_SAFE_FREE(uniqueUv); /* Allocate connectivity data, we allocate edges once */ - data->uvedges = MEM_mallocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash), + data->uvedges = MEM_callocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash), "uv_brush_edge_connectivity_data"); if (!data->uvedges) { BLI_ghash_free(edgeHash, NULL, NULL); - MEM_freeN(edges); + MEM_SAFE_FREE(edges); uv_sculpt_stroke_exit(C, op); return NULL; } /* fill the edges with data */ - i = 0; - GHASH_ITER (gh_iter, edgeHash) { - data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); + { + int i = 0; + GHASH_ITER (gh_iter, edgeHash) { + data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); + } + data->totalUvEdges = BLI_ghash_len(edgeHash); } - data->totalUvEdges = BLI_ghash_len(edgeHash); /* cleanup temporary stuff */ BLI_ghash_free(edgeHash, NULL, NULL); - MEM_freeN(edges); + MEM_SAFE_FREE(edges); /* transfer boundary edge property to UV's */ - if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) { - for (i = 0; i < data->totalUvEdges; i++) { - if (!data->uvedges[i].flag) { - data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY; - data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY; + for (int i = 0; i < data->totalUvEdges; i++) { + if (!data->uvedges[i].is_interior) { + data->uv[data->uvedges[i].uv1].is_boundary = true; + data->uv[data->uvedges[i].uv2].is_boundary = true; + if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) { + data->uv[data->uvedges[i].uv1].is_locked = true; + data->uv[data->uvedges[i].uv2].is_locked = true; } + set_element_flag(data->uv[data->uvedges[i].uv1].element, MARK_BOUNDARY); + set_element_flag(data->uv[data->uvedges[i].uv2].element, MARK_BOUNDARY); } } + SpaceImage *sima = CTX_wm_space_image(C); + data->constrain_to_bounds = (sima->flag & SI_CLIP_UV); + BKE_image_find_nearest_tile_with_offset(sima->image, co, data->uv_base_offset); + /* Allocate initial selection for grab tool */ if (data->tool == UV_SCULPT_TOOL_GRAB) { float radius, radius_root; UvSculptData *sculptdata = (UvSculptData *)op->customdata; - SpaceImage *sima; int width, height; float aspectRatio; float alpha, zoomx, zoomy; @@ -681,7 +860,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm alpha = BKE_brush_alpha_get(scene, brush); radius = BKE_brush_size_get(scene, brush); - sima = CTX_wm_space_image(C); ED_space_image_get_size(sima, &width, &height); ED_space_image_get_zoom(sima, region, &zoomx, &zoomy); @@ -706,16 +884,16 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm copy_v2_v2(data->initial_stroke->init_coord, co); counter = 0; - - for (i = 0; i < data->totalUniqueUvs; i++) { - float dist, diff[2]; - if (data->uv[i].flag & MARK_BOUNDARY) { + for (int i = 0; i < data->totalUniqueUvs; i++) { + if (data->uv[i].is_locked) { continue; } + float diff[2]; sub_v2_v2v2(diff, data->uv[i].uv, co); diff[1] /= aspectRatio; - if ((dist = dot_v2v2(diff, diff)) <= radius) { + float dist = dot_v2v2(diff, diff); + if (dist <= radius) { float strength; strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); @@ -727,6 +905,9 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm } data->initial_stroke->totalInitialSelected = counter; + if (sima->flag & SI_LIVE_UNWRAP) { + ED_uvedit_live_unwrap_begin(scene, obedit); + } } } |