diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint/curves_sculpt_add.cc')
-rw-r--r-- | source/blender/editors/sculpt_paint/curves_sculpt_add.cc | 277 |
1 files changed, 153 insertions, 124 deletions
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 26145a386f5..b5d739ae08e 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -23,6 +23,8 @@ #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" @@ -42,6 +44,8 @@ #include "WM_api.h" +#include "DEG_depsgraph_query.h" + /** * The code below uses a suffix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -80,13 +84,17 @@ 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_; + 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; @@ -99,14 +107,6 @@ struct AddOperationExecutor { CurvesSurfaceTransforms transforms_; - BVHTreeFromMesh surface_bvh_; - - struct AddedPoints { - Vector<float3> positions_cu; - Vector<float3> bary_coords; - Vector<int> looptri_indices; - }; - AddOperationExecutor(const bContext &C) : ctx_(C) { } @@ -114,19 +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; } - transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); - surface_ob_ = curves_id_->surface; - surface_ = static_cast<Mesh *>(surface_ob_->data); + 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; + } + + 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; + } + 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); @@ -143,56 +164,63 @@ struct AddOperationExecutor { 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_)}; - /* 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; } - /* Find UV map. */ - VArraySpan<float2> surface_uv_map; - if (curves_id_->surface_uv_map != nullptr) { - const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_); - surface_uv_map = surface_attributes.lookup<float2>(curves_id_->surface_uv_map, - ATTR_DOMAIN_CORNER); - } + const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(&surface_orig), + BKE_mesh_runtime_looptri_len(&surface_orig)}; /* Find normals. */ - if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_); + 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_->ldata, CD_NORMAL)), - surface_->totloop}; + 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.root_positions_cu = added_points.positions_cu; - add_inputs.bary_coords = added_points.bary_coords; - add_inputs.looptri_indices = added_points.looptri_indices; + 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 & @@ -201,13 +229,10 @@ struct AddOperationExecutor { 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.surface = surface_; - add_inputs.surface_bvh = &surface_bvh_; - add_inputs.surface_looptris = surface_looptris_; - add_inputs.surface_uv_map = surface_uv_map; + 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; - add_inputs.curves_to_surface_mat = transforms_.curves_to_surface; - add_inputs.surface_to_curves_normal_mat = transforms_.surface_to_curves_normal; if (add_inputs.interpolate_length || add_inputs.interpolate_shape || add_inputs.interpolate_point_count) { @@ -215,17 +240,22 @@ struct AddOperationExecutor { add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_; } - geometry::add_curves_on_mesh(*curves_, add_inputs); + 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_->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( @@ -234,15 +264,15 @@ struct AddOperationExecutor { 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 = transforms_.curves_to_surface * brush_transform; - this->sample_in_center(r_added_points, transform * ray_start_cu, transform * ray_end_cu); + 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) { @@ -251,58 +281,61 @@ 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 = bke::mesh_surface_sample::compute_bary_coord_in_triangle( - *surface_, surface_looptris_[looptri_index], brush_pos_su); + surface_verts_eval_, surface_loops_eval_, looptri, brush_pos_su); - const float3 brush_pos_cu = transforms_.surface_to_curves * 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 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 int missing_amount = add_amount_ + old_amount - r_added_points.bary_coords.size(); + 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_, - surface_bvh_, + *surface_eval_, + surface_bvh_eval_, brush_pos_re_, brush_radius_re_, [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) { @@ -317,11 +350,14 @@ struct AddOperationExecutor { use_front_face_, add_amount_, missing_amount, - r_added_points.bary_coords, - r_added_points.looptri_indices, - r_added_points.positions_cu); - for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) { - pos = transforms_.surface_to_curves * pos; + 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); } } } @@ -329,13 +365,13 @@ struct AddOperationExecutor { /** * 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) { const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, *ctx_.region, *ctx_.v3d, transforms_, - surface_bvh_, + surface_bvh_eval_, brush_pos_re_, brush_radius_re_); if (!brush_3d.has_value()) { @@ -355,7 +391,7 @@ struct AddOperationExecutor { 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 = transforms_.curves_to_surface * brush_transform; @@ -365,13 +401,12 @@ struct AddOperationExecutor { const float brush_radius_su = transform_brush_radius( transform, brush_3d->position_cu, brush_3d->radius_cu); - this->sample_spherical( - rng, r_added_points, brush_pos_su, brush_radius_su, view_direction_su); + 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, + Vector<float2> &r_sampled_uvs, const float3 &brush_pos_su, const float brush_radius_su, const float3 &view_direction_su) @@ -379,32 +414,32 @@ struct AddOperationExecutor { 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, 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); }); } @@ -418,42 +453,45 @@ struct AddOperationExecutor { 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; } + 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_, - looptri_indices, + *surface_eval_, + selected_looptri_indices, brush_pos_su, brush_radius_su, approximate_density_su, - r_added_points.bary_coords, - r_added_points.looptri_indices, - r_added_points.positions_cu); - for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) { - pos = transforms_.surface_to_curves * pos; + 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_); @@ -467,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>(); } |