Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
| Show All 32 Lines | |||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "DNA_screen_types.h" | #include "DNA_screen_types.h" | ||||
| #include "DNA_space_types.h" | #include "DNA_space_types.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_view3d.h" | #include "ED_view3d.h" | ||||
| #include "GEO_constraint_solver.hh" | |||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| /** | /** | ||||
| * The code below uses a prefix naming convention to indicate the coordinate space: | * The code below uses a prefix naming convention to indicate the coordinate space: | ||||
| * cu: Local space of the curves object that is being edited. | * cu: Local space of the curves object that is being edited. | ||||
| * su: Local space of the surface object. | * su: Local space of the surface object. | ||||
| * wo: World space. | * wo: World space. | ||||
| * re: 2D coordinates within the region. | * re: 2D coordinates within the region. | ||||
| */ | */ | ||||
| namespace blender::ed::sculpt_paint { | namespace blender::ed::sculpt_paint { | ||||
| using blender::bke::CurvesGeometry; | using blender::bke::CurvesGeometry; | ||||
| using geometry::ConstraintSolver; | |||||
| using threading::EnumerableThreadSpecific; | using threading::EnumerableThreadSpecific; | ||||
| /** | /** | ||||
| * Moves individual points under the brush and does a length preservation step afterwards. | * Moves individual points under the brush and does a length preservation step afterwards. | ||||
| */ | */ | ||||
| class CombOperation : public CurvesSculptStrokeOperation { | class CombOperation : public CurvesSculptStrokeOperation { | ||||
| private: | private: | ||||
| /** Last mouse position. */ | /** Last mouse position. */ | ||||
| float2 brush_pos_last_re_; | float2 brush_pos_last_re_; | ||||
| /** Only used when a 3D brush is used. */ | /** Only used when a 3D brush is used. */ | ||||
| CurvesBrush3D brush_3d_; | CurvesBrush3D brush_3d_; | ||||
| /** Length of each segment indexed by the index of the first point in the segment. */ | /** Solver for length and contact constraints. */ | ||||
| Array<float> segment_lengths_cu_; | ConstraintSolver constraint_solver_; | ||||
| friend struct CombOperationExecutor; | friend struct CombOperationExecutor; | ||||
| public: | public: | ||||
| void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; | void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; | ||||
| }; | }; | ||||
| /** | /** | ||||
| Show All 14 Lines | struct CombOperationExecutor { | ||||
| Object *curves_ob_orig_ = nullptr; | Object *curves_ob_orig_ = nullptr; | ||||
| Curves *curves_id_orig_ = nullptr; | Curves *curves_id_orig_ = nullptr; | ||||
| CurvesGeometry *curves_orig_ = nullptr; | CurvesGeometry *curves_orig_ = nullptr; | ||||
| VArray<float> point_factors_; | VArray<float> point_factors_; | ||||
| Vector<int64_t> selected_curve_indices_; | Vector<int64_t> selected_curve_indices_; | ||||
| IndexMask curve_selection_; | IndexMask curve_selection_; | ||||
| Array<float3> start_positions_; | |||||
| float2 brush_pos_prev_re_; | float2 brush_pos_prev_re_; | ||||
| float2 brush_pos_re_; | float2 brush_pos_re_; | ||||
| float2 brush_pos_diff_re_; | float2 brush_pos_diff_re_; | ||||
| CurvesSurfaceTransforms transforms_; | CurvesSurfaceTransforms transforms_; | ||||
| CombOperationExecutor(const bContext &C) : ctx_(C) | CombOperationExecutor(const bContext &C) : ctx_(C) | ||||
| Show All 29 Lines | void execute(CombOperation &self, const bContext &C, const StrokeExtension &stroke_extension) | ||||
| brush_pos_prev_re_ = self_->brush_pos_last_re_; | brush_pos_prev_re_ = self_->brush_pos_last_re_; | ||||
| brush_pos_re_ = stroke_extension.mouse_position; | brush_pos_re_ = stroke_extension.mouse_position; | ||||
| brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; | brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; | ||||
| if (stroke_extension.is_first) { | if (stroke_extension.is_first) { | ||||
| if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { | if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { | ||||
| this->initialize_spherical_brush_reference_point(); | this->initialize_spherical_brush_reference_point(); | ||||
| } | } | ||||
| this->initialize_segment_lengths(); | |||||
| ConstraintSolver::Params params; | |||||
| params.use_collision_constraints = curves_id_orig_->flag & CV_SCULPT_COLLISION_ENABLED; | |||||
| self_->constraint_solver_.initialize(params, *curves_orig_, curve_selection_); | |||||
| /* Combing does nothing when there is no mouse movement, so return directly. */ | /* Combing does nothing when there is no mouse movement, so return directly. */ | ||||
| return; | return; | ||||
| } | } | ||||
| EnumerableThreadSpecific<Vector<int>> changed_curves; | start_positions_ = curves_orig_->positions(); | ||||
| EnumerableThreadSpecific<Vector<int64_t>> changed_curves; | |||||
| if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { | if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { | ||||
| this->comb_projected_with_symmetry(changed_curves); | this->comb_projected_with_symmetry(changed_curves); | ||||
| } | } | ||||
| else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { | else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { | ||||
| this->comb_spherical_with_symmetry(changed_curves); | this->comb_spherical_with_symmetry(changed_curves); | ||||
| } | } | ||||
| else { | else { | ||||
| BLI_assert_unreachable(); | BLI_assert_unreachable(); | ||||
| } | } | ||||
| this->restore_segment_lengths(changed_curves); | const Mesh *surface = curves_id_orig_->surface && curves_id_orig_->surface->type == OB_MESH ? | ||||
| static_cast<Mesh *>(curves_id_orig_->surface->data) : | |||||
| nullptr; | |||||
| /* Combine TLS curves into a single array for redistributing thread load. | |||||
lukastoenne: This is probably not correct:
- Doing a plain for loop over the TLS vectors causes memleaks in… | |||||
Not Done Inline ActionsHmm, I don't observe memory leaks here. I don't have much advice to offer now, but I can look into it more later HooglyBoogly: Hmm, I don't observe memory leaks here. I don't have much advice to offer now, but I can look… | |||||
Not Done Inline ActionsIt's probably not a leak actually. Specifically i'm getting access violations on exit in optimized builds (haven't been able to repro in debug builds yet):
lukastoenne: It's probably not a leak actually. Specifically i'm getting access violations on exit in… | |||||
| * Brush filtering results in TLS lists with potentially very uneven sizes. */ | |||||
| int totcurves = 0; | |||||
| for (auto curves : changed_curves) { | |||||
| totcurves += curves.size(); | |||||
| } | |||||
| Array<int64_t> all_changed_curves(totcurves); | |||||
| totcurves = 0; | |||||
| for (auto curves : changed_curves) { | |||||
| all_changed_curves.as_mutable_span().slice(totcurves, curves.size()).copy_from(curves); | |||||
| totcurves += curves.size(); | |||||
| }; | |||||
| self_->constraint_solver_.step_curves( | |||||
| *curves_orig_, surface, transforms_, start_positions_, IndexMask(all_changed_curves)); | |||||
| curves_orig_->tag_positions_changed(); | curves_orig_->tag_positions_changed(); | ||||
| DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); | DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); | ||||
| WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); | WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); | ||||
| ED_region_tag_redraw(ctx_.region); | ED_region_tag_redraw(ctx_.region); | ||||
| } | } | ||||
| /** | /** | ||||
| * Do combing in screen space. | * Do combing in screen space. | ||||
| */ | */ | ||||
| void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) | void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int64_t>> &r_changed_curves) | ||||
| { | { | ||||
| const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( | const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( | ||||
| eCurvesSymmetryType(curves_id_orig_->symmetry)); | eCurvesSymmetryType(curves_id_orig_->symmetry)); | ||||
| for (const float4x4 &brush_transform : symmetry_brush_transforms) { | for (const float4x4 &brush_transform : symmetry_brush_transforms) { | ||||
| this->comb_projected(r_changed_curves, brush_transform); | this->comb_projected(r_changed_curves, brush_transform); | ||||
| } | } | ||||
| } | } | ||||
| void comb_projected(EnumerableThreadSpecific<Vector<int>> &r_changed_curves, | void comb_projected(EnumerableThreadSpecific<Vector<int64_t>> &r_changed_curves, | ||||
| const float4x4 &brush_transform) | const float4x4 &brush_transform) | ||||
| { | { | ||||
| const float4x4 brush_transform_inv = brush_transform.inverted(); | const float4x4 brush_transform_inv = brush_transform.inverted(); | ||||
| MutableSpan<float3> positions_cu_orig = curves_orig_->positions_for_write(); | MutableSpan<float3> positions_cu_orig = curves_orig_->positions_for_write(); | ||||
| const bke::crazyspace::GeometryDeformation deformation = | const bke::crazyspace::GeometryDeformation deformation = | ||||
| bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); | bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); | ||||
| float4x4 projection; | float4x4 projection; | ||||
| ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, 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_re = brush_radius_base_re_ * brush_radius_factor_; | ||||
| const float brush_radius_sq_re = pow2f(brush_radius_re); | const float brush_radius_sq_re = pow2f(brush_radius_re); | ||||
| threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { | threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { | ||||
| Vector<int> &local_changed_curves = r_changed_curves.local(); | Vector<int64_t> &local_changed_curves = r_changed_curves.local(); | ||||
| for (const int curve_i : curve_selection_.slice(range)) { | for (const int curve_i : curve_selection_.slice(range)) { | ||||
| bool curve_changed = false; | bool curve_changed = false; | ||||
| const IndexRange points = curves_orig_->points_for_curve(curve_i); | const IndexRange points = curves_orig_->points_for_curve(curve_i); | ||||
| for (const int point_i : points.drop_front(1)) { | for (const int point_i : points.drop_front(1)) { | ||||
| const float3 old_pos_cu = deformation.positions[point_i]; | const float3 old_pos_cu = deformation.positions[point_i]; | ||||
| const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; | const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; | ||||
| /* Find the position of the point in screen space. */ | /* Find the position of the point in screen space. */ | ||||
| Show All 39 Lines | threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| /** | /** | ||||
| * Do combing in 3D space. | * Do combing in 3D space. | ||||
| */ | */ | ||||
| void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) | void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int64_t>> &r_changed_curves) | ||||
| { | { | ||||
| float4x4 projection; | float4x4 projection; | ||||
| ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); | ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); | ||||
| float3 brush_start_wo, brush_end_wo; | float3 brush_start_wo, brush_end_wo; | ||||
| ED_view3d_win_to_3d(ctx_.v3d, | ED_view3d_win_to_3d(ctx_.v3d, | ||||
| ctx_.region, | ctx_.region, | ||||
| transforms_.curves_to_world * self_->brush_3d_.position_cu, | transforms_.curves_to_world * self_->brush_3d_.position_cu, | ||||
| Show All 14 Lines | void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int64_t>> &r_changed_curves) | ||||
| for (const float4x4 &brush_transform : symmetry_brush_transforms) { | for (const float4x4 &brush_transform : symmetry_brush_transforms) { | ||||
| this->comb_spherical(r_changed_curves, | this->comb_spherical(r_changed_curves, | ||||
| brush_transform * brush_start_cu, | brush_transform * brush_start_cu, | ||||
| brush_transform * brush_end_cu, | brush_transform * brush_end_cu, | ||||
| brush_radius_cu); | brush_radius_cu); | ||||
| } | } | ||||
| } | } | ||||
| void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves, | void comb_spherical(EnumerableThreadSpecific<Vector<int64_t>> &r_changed_curves, | ||||
| const float3 &brush_start_cu, | const float3 &brush_start_cu, | ||||
| const float3 &brush_end_cu, | const float3 &brush_end_cu, | ||||
| const float brush_radius_cu) | const float brush_radius_cu) | ||||
| { | { | ||||
| MutableSpan<float3> positions_cu = curves_orig_->positions_for_write(); | MutableSpan<float3> positions_cu = curves_orig_->positions_for_write(); | ||||
| const float brush_radius_sq_cu = pow2f(brush_radius_cu); | const float brush_radius_sq_cu = pow2f(brush_radius_cu); | ||||
| const float3 brush_diff_cu = brush_end_cu - brush_start_cu; | const float3 brush_diff_cu = brush_end_cu - brush_start_cu; | ||||
| const bke::crazyspace::GeometryDeformation deformation = | const bke::crazyspace::GeometryDeformation deformation = | ||||
| bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); | bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); | ||||
| threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { | threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { | ||||
| Vector<int> &local_changed_curves = r_changed_curves.local(); | Vector<int64_t> &local_changed_curves = r_changed_curves.local(); | ||||
| for (const int curve_i : curve_selection_.slice(range)) { | for (const int curve_i : curve_selection_.slice(range)) { | ||||
| bool curve_changed = false; | bool curve_changed = false; | ||||
| const IndexRange points = curves_orig_->points_for_curve(curve_i); | const IndexRange points = curves_orig_->points_for_curve(curve_i); | ||||
| for (const int point_i : points.drop_front(1)) { | for (const int point_i : points.drop_front(1)) { | ||||
| const float3 pos_old_cu = deformation.positions[point_i]; | const float3 pos_old_cu = deformation.positions[point_i]; | ||||
| /* Compute distance to the brush. */ | /* Compute distance to the brush. */ | ||||
| const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3( | const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3( | ||||
| Show All 37 Lines | std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(*ctx_.depsgraph, | ||||
| *ctx_.rv3d, | *ctx_.rv3d, | ||||
| *curves_ob_orig_, | *curves_ob_orig_, | ||||
| brush_pos_re_, | brush_pos_re_, | ||||
| brush_radius_base_re_); | brush_radius_base_re_); | ||||
| if (brush_3d.has_value()) { | if (brush_3d.has_value()) { | ||||
| self_->brush_3d_ = *brush_3d; | self_->brush_3d_ = *brush_3d; | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Remember the initial length of all curve segments. This allows restoring the length after | |||||
| * combing. | |||||
| */ | |||||
| void initialize_segment_lengths() | |||||
| { | |||||
| 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_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]; | |||||
| const float length_cu = math::distance(p1_cu, p2_cu); | |||||
| self_->segment_lengths_cu_[point_i] = length_cu; | |||||
| } | |||||
| } | |||||
| }); | |||||
| } | |||||
| /** | |||||
| * Restore previously stored length for each segment in the changed curves. | |||||
| */ | |||||
| void restore_segment_lengths(EnumerableThreadSpecific<Vector<int>> &changed_curves) | |||||
| { | |||||
| const Span<float> expected_lengths_cu = self_->segment_lengths_cu_; | |||||
| 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_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]; | |||||
| 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 CombOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) | void CombOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) | ||||
| { | { | ||||
| CombOperationExecutor executor{C}; | CombOperationExecutor executor{C}; | ||||
| executor.execute(*this, C, stroke_extension); | executor.execute(*this, C, stroke_extension); | ||||
| } | } | ||||
| std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation() | std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation() | ||||
| { | { | ||||
| return std::make_unique<CombOperation>(); | return std::make_unique<CombOperation>(); | ||||
| } | } | ||||
| } // namespace blender::ed::sculpt_paint | } // namespace blender::ed::sculpt_paint | ||||
This is probably not correct:
Could use some advice here.