Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/curves_sculpt_add.cc
| Show All 12 Lines | |||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "BKE_attribute_math.hh" | #include "BKE_attribute_math.hh" | ||||
| #include "BKE_brush.h" | #include "BKE_brush.h" | ||||
| #include "BKE_bvhutils.h" | #include "BKE_bvhutils.h" | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_curves.hh" | #include "BKE_curves.hh" | ||||
| #include "BKE_curves_utils.hh" | |||||
| #include "BKE_mesh.h" | #include "BKE_mesh.h" | ||||
| #include "BKE_mesh_runtime.h" | #include "BKE_mesh_runtime.h" | ||||
| #include "BKE_paint.h" | #include "BKE_paint.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_spline.hh" | #include "BKE_spline.hh" | ||||
| #include "DNA_brush_enums.h" | #include "DNA_brush_enums.h" | ||||
| #include "DNA_brush_types.h" | #include "DNA_brush_types.h" | ||||
| ▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | struct AddOperationExecutor { | ||||
| BrushCurvesSculptSettings *brush_settings_ = nullptr; | BrushCurvesSculptSettings *brush_settings_ = nullptr; | ||||
| float brush_radius_re_; | float brush_radius_re_; | ||||
| 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 interpolate_point_count_; | |||||
| 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_; | ||||
| BVHTreeFromMesh surface_bvh_; | BVHTreeFromMesh surface_bvh_; | ||||
| int tot_old_curves_; | int tot_old_curves_; | ||||
| int tot_old_points_; | int tot_old_points_; | ||||
| struct AddedPoints { | struct AddedPoints { | ||||
| Vector<float3> positions_cu; | Vector<float3> positions_cu; | ||||
| Vector<float3> bary_coords; | Vector<float3> bary_coords; | ||||
| Vector<int> looptri_indices; | 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>; | |||||
| void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) | void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) | ||||
| { | { | ||||
| self_ = &self; | self_ = &self; | ||||
| depsgraph_ = CTX_data_depsgraph_pointer(C); | depsgraph_ = CTX_data_depsgraph_pointer(C); | ||||
| scene_ = CTX_data_scene(C); | scene_ = CTX_data_scene(C); | ||||
| object_ = CTX_data_active_object(C); | object_ = CTX_data_active_object(C); | ||||
| region_ = CTX_wm_region(C); | region_ = CTX_wm_region(C); | ||||
| v3d_ = CTX_wm_view3d(C); | v3d_ = CTX_wm_view3d(C); | ||||
| Show All 27 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_; | 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; | 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(); | ||||
| if (add_amount_ == 0) { | if (add_amount_ == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| Show All 23 Lines | else { | ||||
| BLI_assert_unreachable(); | BLI_assert_unreachable(); | ||||
| } | } | ||||
| if (added_points.bary_coords.is_empty()) { | if (added_points.bary_coords.is_empty()) { | ||||
| /* No new points have been added. */ | /* No new points have been added. */ | ||||
| return; | return; | ||||
| } | } | ||||
| Array<NeighborsVector> neighbors_per_curve; | |||||
| if (use_interpolation_) { | if (use_interpolation_) { | ||||
| this->ensure_curve_roots_kdtree(); | this->ensure_curve_roots_kdtree(); | ||||
| neighbors_per_curve = this->find_curve_neighbors(added_points); | |||||
| } | } | ||||
| /* Resize to add the new curves, building the offests in the array owned by thge curves. */ | |||||
| 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_; | curves_->resize(curves_->points_num(), curves_->curves_num() + tot_added_curves); | ||||
| if (interpolate_point_count_) { | |||||
| this->initialize_curve_offsets_with_interpolation(neighbors_per_curve); | |||||
| } | |||||
| else { | |||||
| this->initialize_curve_offsets_without_interpolation(constant_points_per_curve_); | |||||
| } | |||||
| curves_->resize(curves_->points_num() + tot_added_points, | /* Resize to add the correct point count calculated as part of building the offsets. */ | ||||
| curves_->curves_num() + tot_added_curves); | curves_->resize(curves_->offsets().last(), curves_->curves_num()); | ||||
| threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); }, | this->initialize_attributes(added_points, neighbors_per_curve); | ||||
| [&]() { this->initialize_attributes(added_points); }); | |||||
| curves_->update_curve_types(); | curves_->update_curve_types(); | ||||
| 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; | ||||
HooglyBoogly: I think I'd recommend doing this a bit differently--
- Resize the curves to the correct number… | |||||
| const float3 &v1 = mesh.mvert[mesh.mloop[looptri.tri[1]].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; | const float3 &v2 = mesh.mvert[mesh.mloop[looptri.tri[2]].v].co; | ||||
| float3 bary_coords; | float3 bary_coords; | ||||
| interp_weights_tri_v3(bary_coords, v0, v1, v2, position); | interp_weights_tri_v3(bary_coords, v0, v1, v2, position); | ||||
| return bary_coords; | return bary_coords; | ||||
| } | } | ||||
| /** | /** | ||||
| ▲ Show 20 Lines • Show All 327 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_with_interpolation(const Span<NeighborsVector> neighbors_per_curve) | ||||
| { | { | ||||
| MutableSpan<int> offsets = curves_->offsets_for_write(); | MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); | ||||
| threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { | |||||
| for (const int i : range) { | attribute_math::DefaultMixer<int> mixer{new_offsets}; | ||||
| const int curve_i = tot_old_curves_ + i; | threading::parallel_for(neighbors_per_curve.index_range(), 1024, [&](IndexRange curves_range) { | ||||
| offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_; | for (const int i : curves_range) { | ||||
| 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(); | |||||
| struct NeighborInfo { | bke::curves::accumulate_counts_to_offsets(new_offsets, tot_old_points_); | ||||
| /* 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>; | |||||
| void initialize_attributes(const AddedPoints &added_points) | void initialize_curve_offsets_without_interpolation(const int points_per_curve) | ||||
| { | { | ||||
| Array<NeighborsVector> neighbors_per_curve; | MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); | ||||
| if (use_interpolation_) { | int offset = tot_old_points_; | ||||
| neighbors_per_curve = this->find_curve_neighbors(added_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()); | 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_); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | 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) | ||||
| { | { | ||||
| 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 IndexRange points = curves_->points_for_curve(tot_old_curves_ + 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(points)); | ||||
| root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| /** | /** | ||||
| * 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) | ||||
| { | { | ||||
| 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 IndexRange points = curves_->points_for_curve(tot_old_curves_ + 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_; | |||||
| 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(points)); | ||||
| root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); | |||||
| continue; | continue; | ||||
| } | } | ||||
| positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu); | positions_cu.slice(points).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.size() - 1.0f)) * length_factor; | ||||
| for (const int j : IndexRange(points_per_curve_)) { | for (const int j : IndexRange(points.size())) { | ||||
| 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; | ||||
| mul_m3_v3(normal_rotation_cu, rotated_relative_coord); | mul_m3_v3(normal_rotation_cu, rotated_relative_coord); | ||||
| positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord; | positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| }; | }; | ||||
| void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) | void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) | ||||
| Show All 19 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.