Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc
| Show All 17 Lines | |||||
| #include "DNA_brush_enums.h" | #include "DNA_brush_enums.h" | ||||
| #include "DNA_brush_types.h" | #include "DNA_brush_types.h" | ||||
| #include "DNA_curves_types.h" | #include "DNA_curves_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 "GEO_constraint_solver.hh" | |||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_view3d.h" | #include "ED_view3d.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 geometry::ConstraintSolver; | |||||
| class PinchOperation : public CurvesSculptStrokeOperation { | class PinchOperation : public CurvesSculptStrokeOperation { | ||||
| private: | private: | ||||
| bool invert_pinch_; | bool invert_pinch_; | ||||
| Array<float> segment_lengths_cu_; | |||||
| /** Solver for length and contact constraints. */ | |||||
| ConstraintSolver constraint_solver_; | |||||
| /** Only used when a 3D brush is used. */ | /** Only used when a 3D brush is used. */ | ||||
| CurvesBrush3D brush_3d_; | CurvesBrush3D brush_3d_; | ||||
| friend struct PinchOperationExecutor; | friend struct PinchOperationExecutor; | ||||
| public: | public: | ||||
| PinchOperation(const bool invert_pinch) : invert_pinch_(invert_pinch) | PinchOperation(const bool invert_pinch) : invert_pinch_(invert_pinch) | ||||
| Show All 9 Lines | struct PinchOperationExecutor { | ||||
| Object *object_ = nullptr; | Object *object_ = nullptr; | ||||
| Curves *curves_id_ = nullptr; | Curves *curves_id_ = nullptr; | ||||
| CurvesGeometry *curves_ = nullptr; | CurvesGeometry *curves_ = 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_; | |||||
| CurvesSurfaceTransforms transforms_; | CurvesSurfaceTransforms transforms_; | ||||
| const CurvesSculpt *curves_sculpt_ = nullptr; | const CurvesSculpt *curves_sculpt_ = nullptr; | ||||
| const Brush *brush_ = nullptr; | const Brush *brush_ = nullptr; | ||||
| float brush_radius_base_re_; | float brush_radius_base_re_; | ||||
| float brush_radius_factor_; | float brush_radius_factor_; | ||||
| float brush_strength_; | float brush_strength_; | ||||
| Show All 30 Lines | void execute(PinchOperation &self, const bContext &C, const StrokeExtension &stroke_extension) | ||||
| point_factors_ = get_point_selection(*curves_id_); | point_factors_ = get_point_selection(*curves_id_); | ||||
| curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); | curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); | ||||
| brush_pos_re_ = stroke_extension.mouse_position; | brush_pos_re_ = stroke_extension.mouse_position; | ||||
| const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( | const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( | ||||
| brush_->falloff_shape); | brush_->falloff_shape); | ||||
| if (stroke_extension.is_first) { | if (stroke_extension.is_first) { | ||||
| this->initialize_segment_lengths(); | |||||
| if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { | if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { | ||||
| self_->brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, | self_->brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, | ||||
| *ctx_.region, | *ctx_.region, | ||||
| *ctx_.v3d, | *ctx_.v3d, | ||||
| *ctx_.rv3d, | *ctx_.rv3d, | ||||
| *object_, | *object_, | ||||
| brush_pos_re_, | brush_pos_re_, | ||||
| brush_radius_base_re_); | brush_radius_base_re_); | ||||
| } | } | ||||
| ConstraintSolver::Params params; | |||||
| params.use_collision_constraints = curves_id_->flag & CV_SCULPT_COLLISION_ENABLED; | |||||
| self_->constraint_solver_.initialize(params, *curves_, curve_selection_); | |||||
| } | } | ||||
| start_positions_ = curves_->positions(); | |||||
| Array<bool> changed_curves(curves_->curves_num(), false); | Array<bool> changed_curves(curves_->curves_num(), false); | ||||
| if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { | if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { | ||||
| this->pinch_projected_with_symmetry(changed_curves); | this->pinch_projected_with_symmetry(changed_curves); | ||||
| } | } | ||||
| else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { | else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { | ||||
| this->pinch_spherical_with_symmetry(changed_curves); | this->pinch_spherical_with_symmetry(changed_curves); | ||||
| } | } | ||||
| else { | else { | ||||
| BLI_assert_unreachable(); | BLI_assert_unreachable(); | ||||
| } | } | ||||
| this->restore_segment_lengths(changed_curves); | /* XXX Dumb array conversion to pass to the constraint solver. | ||||
| * Should become unnecessary once brushes use the same methods for computing weights */ | |||||
| Vector<int64_t> changed_curves_indices; | |||||
| changed_curves_indices.reserve(curves_->curves_num()); | |||||
| for (int64_t curve_i : changed_curves.index_range()) { | |||||
| if (changed_curves[curve_i]) { | |||||
| changed_curves_indices.append(curve_i); | |||||
| } | |||||
| } | |||||
| const Mesh *surface = curves_id_->surface && curves_id_->surface->type == OB_MESH ? | |||||
| static_cast<Mesh *>(curves_id_->surface->data) : | |||||
| nullptr; | |||||
| self_->constraint_solver_.step_curves(*curves_, | |||||
| surface, | |||||
| transforms_, | |||||
| start_positions_, | |||||
| IndexMask(changed_curves_indices)); | |||||
HooglyBoogly: There's no need for the virtual array to take ownership of the changed curve indices… | |||||
Not Done Inline ActionsYeah, i first thought i could reconcile the different ways brushes identify changed curves and just wrap them in a VArray, but ended up having to make copies anyway. What about IndexMask, wouldn't that be the exact use case? lukastoenne: Yeah, i first thought i could reconcile the different ways brushes identify changed curves and… | |||||
| curves_->tag_positions_changed(); | curves_->tag_positions_changed(); | ||||
Done Inline ActionsAll the brushes using constraints (comb, pinch, puff) have slightly different ways of identifying changed curves, which makes it tricky to define a common interface for the solver. Discussed this with @Jacques Lucke (JacquesLucke), we agreed to just keep this conversion until the code is unified a bit more. lukastoenne: All the brushes using constraints (comb, pinch, puff) have slightly different ways of… | |||||
| DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); | DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); | ||||
| WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); | WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); | ||||
| ED_region_tag_redraw(ctx_.region); | ED_region_tag_redraw(ctx_.region); | ||||
| } | } | ||||
| void pinch_projected_with_symmetry(MutableSpan<bool> r_changed_curves) | void pinch_projected_with_symmetry(MutableSpan<bool> r_changed_curves) | ||||
| { | { | ||||
| const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( | const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( | ||||
| ▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { | ||||
| point_i, translation_eval); | point_i, translation_eval); | ||||
| positions_cu[point_i] += translation_orig; | positions_cu[point_i] += translation_orig; | ||||
| r_changed_curves[curve_i] = true; | 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) | void PinchOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) | ||||
| { | { | ||||
| PinchOperationExecutor executor{C}; | PinchOperationExecutor executor{C}; | ||||
| executor.execute(*this, C, stroke_extension); | executor.execute(*this, C, stroke_extension); | ||||
| } | } | ||||
| Show All 12 Lines | |||||
There's no need for the virtual array to take ownership of the changed curve indices, VArray<int>::ForSpan should be better here.
Actually though, it looks like all three uses of the solver could just take a Span<int> argument