Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/curves_sculpt_add.cc
| Show First 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | struct AddOperationExecutor { | ||||
| float2 brush_pos_re_; | float2 brush_pos_re_; | ||||
| bool use_front_face_; | bool use_front_face_; | ||||
| bool interpolate_length_; | bool interpolate_length_; | ||||
| bool interpolate_shape_; | bool interpolate_shape_; | ||||
| bool use_interpolation_; | bool use_interpolation_; | ||||
| float new_curve_length_; | float new_curve_length_; | ||||
| int add_amount_; | int add_amount_; | ||||
| int points_per_curve_; | int constant_points_per_curve_; | ||||
| /** Various matrices to convert between coordinate spaces. */ | /** Various matrices to convert between coordinate spaces. */ | ||||
| float4x4 curves_to_world_mat_; | float4x4 curves_to_world_mat_; | ||||
| float4x4 world_to_curves_mat_; | float4x4 world_to_curves_mat_; | ||||
| float4x4 world_to_surface_mat_; | float4x4 world_to_surface_mat_; | ||||
| float4x4 surface_to_world_mat_; | float4x4 surface_to_world_mat_; | ||||
| float4x4 surface_to_curves_mat_; | float4x4 surface_to_curves_mat_; | ||||
| float4x4 surface_to_curves_normal_mat_; | float4x4 surface_to_curves_normal_mat_; | ||||
| ▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) | ||||
| brush_settings_ = brush_->curves_sculpt_settings; | brush_settings_ = brush_->curves_sculpt_settings; | ||||
| brush_radius_re_ = BKE_brush_size_get(scene_, brush_); | brush_radius_re_ = BKE_brush_size_get(scene_, brush_); | ||||
| brush_pos_re_ = stroke_extension.mouse_position; | brush_pos_re_ = stroke_extension.mouse_position; | ||||
| use_front_face_ = brush_->flag & BRUSH_FRONTFACE; | use_front_face_ = brush_->flag & BRUSH_FRONTFACE; | ||||
| const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( | const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( | ||||
| brush_->falloff_shape); | brush_->falloff_shape); | ||||
| add_amount_ = std::max(0, brush_settings_->add_amount); | add_amount_ = std::max(0, brush_settings_->add_amount); | ||||
| points_per_curve_ = std::max(2, brush_settings_->points_per_curve); | constant_points_per_curve_ = std::max(2, brush_settings_->points_per_curve); | ||||
| interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; | interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; | ||||
| interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; | interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; | ||||
| use_interpolation_ = interpolate_length_ || interpolate_shape_; | use_interpolation_ = interpolate_length_ || interpolate_shape_; | ||||
| new_curve_length_ = brush_settings_->curve_length; | new_curve_length_ = brush_settings_->curve_length; | ||||
| tot_old_curves_ = curves_->curves_num(); | tot_old_curves_ = curves_->curves_num(); | ||||
| tot_old_points_ = curves_->points_num(); | tot_old_points_ = curves_->points_num(); | ||||
| Show All 30 Lines | if (added_points.bary_coords.is_empty()) { | ||||
| /* No new points have been added. */ | /* No new points have been added. */ | ||||
| return; | return; | ||||
| } | } | ||||
| if (use_interpolation_) { | if (use_interpolation_) { | ||||
| this->ensure_curve_roots_kdtree(); | this->ensure_curve_roots_kdtree(); | ||||
| } | } | ||||
| Array<NeighborsVector> neighbors_per_curve; | |||||
| if (use_interpolation_) { | |||||
| neighbors_per_curve = this->find_curve_neighbors(added_points); | |||||
| } | |||||
| Array<int> points_per_curve(added_points.bary_coords.size(), constant_points_per_curve_); | |||||
| Array<int> accumulated_points_per_curve(added_points.bary_coords.size()); | |||||
| int point_counter = 0; | |||||
| for (const int i : accumulated_points_per_curve.index_range()) { | |||||
| point_counter += points_per_curve[i]; | |||||
| accumulated_points_per_curve[i] = point_counter; | |||||
| } | |||||
| const int tot_added_curves = added_points.bary_coords.size(); | const int tot_added_curves = added_points.bary_coords.size(); | ||||
| const int tot_added_points = tot_added_curves * points_per_curve_; | const int tot_added_points = accumulated_points_per_curve.last(); | ||||
| curves_->resize(curves_->points_num() + tot_added_points, | curves_->resize(curves_->points_num() + tot_added_points, | ||||
| curves_->curves_num() + tot_added_curves); | curves_->curves_num() + tot_added_curves); | ||||
| threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); }, | threading::parallel_invoke( | ||||
| [&]() { this->initialize_attributes(added_points); }); | [&]() { this->initialize_curve_offsets(tot_added_curves, accumulated_points_per_curve); }, | ||||
| [&]() { | |||||
| this->initialize_attributes( | |||||
| added_points, neighbors_per_curve, points_per_curve, accumulated_points_per_curve); | |||||
| }); | |||||
HooglyBoogly: I think I'd recommend doing this a bit differently--
- Resize the curves to the correct number… | |||||
| DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); | DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); | ||||
| ED_region_tag_redraw(region_); | ED_region_tag_redraw(region_); | ||||
| } | } | ||||
| float3 get_bary_coords(const Mesh &mesh, const MLoopTri &looptri, const float3 position) const | float3 get_bary_coords(const Mesh &mesh, const MLoopTri &looptri, const float3 position) const | ||||
| { | { | ||||
| const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co; | const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co; | ||||
| ▲ Show 20 Lines • Show All 335 Lines • ▼ Show 20 Lines | if (self_->curve_roots_kdtree_ == nullptr) { | ||||
| const int root_point_i = curves_->offsets()[curve_i]; | const int root_point_i = curves_->offsets()[curve_i]; | ||||
| const float3 &root_pos_cu = curves_->positions()[root_point_i]; | const float3 &root_pos_cu = curves_->positions()[root_point_i]; | ||||
| BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu); | BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu); | ||||
| } | } | ||||
| BLI_kdtree_3d_balance(self_->curve_roots_kdtree_); | BLI_kdtree_3d_balance(self_->curve_roots_kdtree_); | ||||
| } | } | ||||
| } | } | ||||
| void initialize_curve_offsets(const int tot_added_curves) | void initialize_curve_offsets(const int tot_added_curves, | ||||
| const Span<int> accumulated_points_per_curve) | |||||
| { | { | ||||
| MutableSpan<int> offsets = curves_->offsets_for_write(); | MutableSpan<int> offsets = curves_->offsets_for_write(); | ||||
| threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { | threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { | ||||
| for (const int i : range) { | for (const int i : range) { | ||||
| const int curve_i = tot_old_curves_ + i; | const int curve_i = tot_old_curves_ + i; | ||||
| offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_; | offsets[curve_i + 1] = tot_old_points_ + accumulated_points_per_curve[i]; | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| struct NeighborInfo { | struct NeighborInfo { | ||||
| /* Curve index of the neighbor. */ | /* Curve index of the neighbor. */ | ||||
| int index; | int index; | ||||
| /* The weights of all neighbors of a new curve add up to 1. */ | /* The weights of all neighbors of a new curve add up to 1. */ | ||||
| float weight; | float weight; | ||||
| }; | }; | ||||
| static constexpr int max_neighbors = 5; | static constexpr int max_neighbors = 5; | ||||
| using NeighborsVector = Vector<NeighborInfo, max_neighbors>; | using NeighborsVector = Vector<NeighborInfo, max_neighbors>; | ||||
| void initialize_attributes(const AddedPoints &added_points) | void initialize_attributes(const AddedPoints &added_points, | ||||
| const Span<NeighborsVector> neighbors_per_curve, | |||||
| const Span<int> points_per_curve, | |||||
| const Span<int> accumulated_points_per_curve) | |||||
| { | { | ||||
| Array<NeighborsVector> neighbors_per_curve; | |||||
| if (use_interpolation_) { | |||||
| neighbors_per_curve = this->find_curve_neighbors(added_points); | |||||
| } | |||||
| Array<float> new_lengths_cu(added_points.bary_coords.size()); | Array<float> new_lengths_cu(added_points.bary_coords.size()); | ||||
| if (interpolate_length_) { | if (interpolate_length_) { | ||||
| this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); | this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); | ||||
| } | } | ||||
| else { | else { | ||||
| new_lengths_cu.fill(new_curve_length_); | new_lengths_cu.fill(new_curve_length_); | ||||
| } | } | ||||
| Array<float3> new_normals_su = this->compute_normals_for_added_curves_su(added_points); | Array<float3> new_normals_su = this->compute_normals_for_added_curves_su(added_points); | ||||
| this->initialize_surface_attachment(added_points); | this->initialize_surface_attachment(added_points); | ||||
| if (interpolate_shape_) { | if (interpolate_shape_) { | ||||
| this->initialize_position_with_interpolation( | this->initialize_position_with_interpolation(added_points, | ||||
| added_points, neighbors_per_curve, new_normals_su, new_lengths_cu); | neighbors_per_curve, | ||||
| new_normals_su, | |||||
| new_lengths_cu, | |||||
| points_per_curve, | |||||
| accumulated_points_per_curve); | |||||
| } | } | ||||
| else { | else { | ||||
| this->initialize_position_without_interpolation( | this->initialize_position_without_interpolation(added_points, | ||||
| added_points, new_lengths_cu, new_normals_su); | new_lengths_cu, | ||||
| new_normals_su, | |||||
| points_per_curve, | |||||
| accumulated_points_per_curve); | |||||
| } | } | ||||
| } | } | ||||
| Array<NeighborsVector> find_curve_neighbors(const AddedPoints &added_points) | Array<NeighborsVector> find_curve_neighbors(const AddedPoints &added_points) | ||||
| { | { | ||||
| const int tot_added_curves = added_points.bary_coords.size(); | const int tot_added_curves = added_points.bary_coords.size(); | ||||
| Array<NeighborsVector> neighbors_per_curve(tot_added_curves); | Array<NeighborsVector> neighbors_per_curve(tot_added_curves); | ||||
| threading::parallel_for(IndexRange(tot_added_curves), 128, [&](const IndexRange range) { | threading::parallel_for(IndexRange(tot_added_curves), 128, [&](const IndexRange range) { | ||||
| ▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | threading::parallel_for( | ||||
| }); | }); | ||||
| } | } | ||||
| /** | /** | ||||
| * Initialize new curves so that they are just a straight line in the normal direction. | * Initialize new curves so that they are just a straight line in the normal direction. | ||||
| */ | */ | ||||
| void initialize_position_without_interpolation(const AddedPoints &added_points, | void initialize_position_without_interpolation(const AddedPoints &added_points, | ||||
| const Span<float> lengths_cu, | const Span<float> lengths_cu, | ||||
| const MutableSpan<float3> normals_su) | const MutableSpan<float3> normals_su, | ||||
| const Span<int> points_per_curve, | |||||
| const Span<int> accumulated_points_per_curve) | |||||
| { | { | ||||
| MutableSpan<float3> positions_cu = curves_->positions_for_write(); | MutableSpan<float3> positions_cu = curves_->positions_for_write(); | ||||
| threading::parallel_for( | threading::parallel_for( | ||||
| added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { | added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { | ||||
| for (const int i : range) { | for (const int i : range) { | ||||
| const int first_point_i = tot_old_points_ + i * points_per_curve_; | const int first_point_i = tot_old_points_ + accumulated_points_per_curve[i] - | ||||
| points_per_curve[i]; | |||||
| const float3 &root_cu = added_points.positions_cu[i]; | const float3 &root_cu = added_points.positions_cu[i]; | ||||
| const float length = lengths_cu[i]; | const float length = lengths_cu[i]; | ||||
| const float3 &normal_su = normals_su[i]; | const float3 &normal_su = normals_su[i]; | ||||
| const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); | const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); | ||||
| const float3 tip_cu = root_cu + length * normal_cu; | const float3 tip_cu = root_cu + length * normal_cu; | ||||
| initialize_straight_curve_positions( | initialize_straight_curve_positions( | ||||
| root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); | root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve[i])); | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| /** | /** | ||||
| * Use neighboring curves to determine the shape. | * Use neighboring curves to determine the shape. | ||||
| */ | */ | ||||
| void initialize_position_with_interpolation(const AddedPoints &added_points, | void initialize_position_with_interpolation(const AddedPoints &added_points, | ||||
| const Span<NeighborsVector> neighbors_per_curve, | const Span<NeighborsVector> neighbors_per_curve, | ||||
| const Span<float3> new_normals_su, | const Span<float3> new_normals_su, | ||||
| const Span<float> new_lengths_cu) | const Span<float> new_lengths_cu, | ||||
| const Span<int> points_per_curve, | |||||
| const Span<int> accumulated_points_per_curve) | |||||
| { | { | ||||
| MutableSpan<float3> positions_cu = curves_->positions_for_write(); | MutableSpan<float3> positions_cu = curves_->positions_for_write(); | ||||
| const VArray_Span<int> surface_triangle_indices{curves_->surface_triangle_indices()}; | const VArray_Span<int> surface_triangle_indices{curves_->surface_triangle_indices()}; | ||||
| const Span<float2> surface_triangle_coords = curves_->surface_triangle_coords(); | const Span<float2> surface_triangle_coords = curves_->surface_triangle_coords(); | ||||
| threading::parallel_for( | threading::parallel_for( | ||||
| added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { | added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { | ||||
| for (const int i : range) { | for (const int i : range) { | ||||
| const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; | const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; | ||||
| const int points_in_curve = points_per_curve[i]; | |||||
| const float length_cu = new_lengths_cu[i]; | const float length_cu = new_lengths_cu[i]; | ||||
| const float3 &normal_su = new_normals_su[i]; | const float3 &normal_su = new_normals_su[i]; | ||||
| const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); | const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); | ||||
| const float3 &root_cu = added_points.positions_cu[i]; | const float3 &root_cu = added_points.positions_cu[i]; | ||||
| const int first_point_i = tot_old_points_ + i * points_per_curve_; | const int first_point_i = tot_old_points_ + accumulated_points_per_curve[i] - | ||||
| points_in_curve; | |||||
| if (neighbors.is_empty()) { | if (neighbors.is_empty()) { | ||||
| /* If there are no neighbors, just make a straight line. */ | /* If there are no neighbors, just make a straight line. */ | ||||
| const float3 tip_cu = root_cu + length_cu * normal_cu; | const float3 tip_cu = root_cu + length_cu * normal_cu; | ||||
| initialize_straight_curve_positions( | initialize_straight_curve_positions( | ||||
| root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); | root_cu, tip_cu, positions_cu.slice(first_point_i, points_in_curve)); | ||||
| continue; | continue; | ||||
| } | } | ||||
| positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu); | positions_cu.slice(first_point_i, points_in_curve).fill(root_cu); | ||||
| for (const NeighborInfo &neighbor : neighbors) { | for (const NeighborInfo &neighbor : neighbors) { | ||||
| const int neighbor_curve_i = neighbor.index; | const int neighbor_curve_i = neighbor.index; | ||||
| const int neighbor_looptri_index = surface_triangle_indices[neighbor_curve_i]; | const int neighbor_looptri_index = surface_triangle_indices[neighbor_curve_i]; | ||||
| float3 neighbor_bary_coord{surface_triangle_coords[neighbor_curve_i]}; | float3 neighbor_bary_coord{surface_triangle_coords[neighbor_curve_i]}; | ||||
| neighbor_bary_coord.z = 1.0f - neighbor_bary_coord.x - neighbor_bary_coord.y; | neighbor_bary_coord.z = 1.0f - neighbor_bary_coord.x - neighbor_bary_coord.y; | ||||
| Show All 17 Lines | threading::parallel_for( | ||||
| PolySpline neighbor_spline; | PolySpline neighbor_spline; | ||||
| neighbor_spline.resize(neighbor_points.size()); | neighbor_spline.resize(neighbor_points.size()); | ||||
| neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points)); | neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points)); | ||||
| neighbor_spline.mark_cache_invalid(); | neighbor_spline.mark_cache_invalid(); | ||||
| const float neighbor_length_cu = neighbor_spline.length(); | const float neighbor_length_cu = neighbor_spline.length(); | ||||
| const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); | const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); | ||||
| const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor; | const float resample_factor = (1.0f / (points_in_curve - 1.0f)) * length_factor; | ||||
| for (const int j : IndexRange(points_per_curve_)) { | for (const int j : IndexRange(points_in_curve)) { | ||||
| const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( | const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( | ||||
| j * resample_factor); | j * resample_factor); | ||||
| const float index_factor = lookup.evaluated_index + lookup.factor; | const float index_factor = lookup.evaluated_index + lookup.factor; | ||||
| float3 p; | float3 p; | ||||
| neighbor_spline.sample_with_index_factors<float3>( | neighbor_spline.sample_with_index_factors<float3>( | ||||
| neighbor_spline.positions(), {&index_factor, 1}, {&p, 1}); | neighbor_spline.positions(), {&index_factor, 1}, {&p, 1}); | ||||
| const float3 relative_coord = p - neighbor_root_cu; | const float3 relative_coord = p - neighbor_root_cu; | ||||
| float3 rotated_relative_coord = relative_coord; | float3 rotated_relative_coord = relative_coord; | ||||
| Show All 29 Lines | |||||
I think I'd recommend doing this a bit differently--
I like how that workflow is basically standardized, the same thing can be done whenever creating CurvesGeometry with a known size for every curve.