Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/curves_sculpt_add.cc
| Show All 13 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_curves_utils.hh" | ||||
| #include "BKE_geometry_set.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 60 Lines • ▼ Show 20 Lines | struct AddOperationExecutor { | ||||
| Object *object_ = nullptr; | Object *object_ = nullptr; | ||||
| Curves *curves_id_ = nullptr; | Curves *curves_id_ = nullptr; | ||||
| CurvesGeometry *curves_ = nullptr; | CurvesGeometry *curves_ = nullptr; | ||||
| Object *surface_ob_ = nullptr; | Object *surface_ob_ = nullptr; | ||||
| Mesh *surface_ = nullptr; | Mesh *surface_ = nullptr; | ||||
| Span<MLoopTri> surface_looptris_; | Span<MLoopTri> surface_looptris_; | ||||
| Span<float3> corner_normals_su_; | Span<float3> corner_normals_su_; | ||||
| VArray_Span<float2> surface_uv_map_; | |||||
| const CurvesSculpt *curves_sculpt_ = nullptr; | const CurvesSculpt *curves_sculpt_ = nullptr; | ||||
| const Brush *brush_ = nullptr; | const Brush *brush_ = nullptr; | ||||
| const BrushCurvesSculptSettings *brush_settings_ = nullptr; | const 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 interpolate_point_count_; | ||||
| bool use_interpolation_; | bool use_interpolation_; | ||||
| float new_curve_length_; | float new_curve_length_; | ||||
| int add_amount_; | int add_amount_; | ||||
| int constant_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 curves_to_surface_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_; | ||||
| Show All 35 Lines | void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension) | ||||
| world_to_curves_mat_ = curves_to_world_mat_.inverted(); | world_to_curves_mat_ = curves_to_world_mat_.inverted(); | ||||
| surface_ob_ = curves_id_->surface; | surface_ob_ = curves_id_->surface; | ||||
| surface_ = static_cast<Mesh *>(surface_ob_->data); | surface_ = static_cast<Mesh *>(surface_ob_->data); | ||||
| surface_to_world_mat_ = surface_ob_->obmat; | surface_to_world_mat_ = surface_ob_->obmat; | ||||
| world_to_surface_mat_ = surface_to_world_mat_.inverted(); | world_to_surface_mat_ = surface_to_world_mat_.inverted(); | ||||
| surface_to_curves_mat_ = world_to_curves_mat_ * surface_to_world_mat_; | surface_to_curves_mat_ = world_to_curves_mat_ * surface_to_world_mat_; | ||||
| surface_to_curves_normal_mat_ = surface_to_curves_mat_.inverted().transposed(); | surface_to_curves_normal_mat_ = surface_to_curves_mat_.inverted().transposed(); | ||||
| curves_to_surface_mat_ = curves_to_world_mat_ * world_to_surface_mat_; | |||||
| if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { | if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { | ||||
| BKE_mesh_calc_normals_split(surface_); | BKE_mesh_calc_normals_split(surface_); | ||||
| } | } | ||||
| corner_normals_su_ = { | corner_normals_su_ = { | ||||
| reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), | reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), | ||||
| surface_->totloop}; | surface_->totloop}; | ||||
| Show All 27 Lines | void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension) | ||||
| RandomNumberGenerator rng{*(uint32_t *)(&time)}; | RandomNumberGenerator rng{*(uint32_t *)(&time)}; | ||||
| BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); | BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); | ||||
| BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); | BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); | ||||
| surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), | surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), | ||||
| BKE_mesh_runtime_looptri_len(surface_)}; | BKE_mesh_runtime_looptri_len(surface_)}; | ||||
| if (curves_id_->surface_uv_map != nullptr) { | |||||
| MeshComponent surface_component; | |||||
| surface_component.replace(surface_, GeometryOwnershipType::ReadOnly); | |||||
| surface_uv_map_ = surface_component | |||||
| .attribute_try_get_for_read(curves_id_->surface_uv_map, | |||||
| ATTR_DOMAIN_CORNER) | |||||
| .typed<float2>(); | |||||
| } | |||||
| /* Sample points on the surface using one of multiple strategies. */ | /* Sample points on the surface using one of multiple strategies. */ | ||||
| AddedPoints added_points; | AddedPoints added_points; | ||||
| if (add_amount_ == 1) { | if (add_amount_ == 1) { | ||||
| this->sample_in_center_with_symmetry(added_points); | this->sample_in_center_with_symmetry(added_points); | ||||
| } | } | ||||
| else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { | else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { | ||||
| this->sample_projected_with_symmetry(rng, added_points); | this->sample_projected_with_symmetry(rng, added_points); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 428 Lines • ▼ Show 20 Lines | void initialize_attributes(const AddedPoints &added_points, | ||||
| 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); | ||||
| if (!surface_uv_map_.is_empty()) { | |||||
| this->initialize_surface_attachment(added_points); | this->initialize_surface_attachment(added_points); | ||||
| } | |||||
| this->fill_new_selection(); | this->fill_new_selection(); | ||||
| if (interpolate_shape_) { | if (interpolate_shape_) { | ||||
| this->initialize_position_with_interpolation( | this->initialize_position_with_interpolation( | ||||
| added_points, neighbors_per_curve, new_normals_su, new_lengths_cu); | added_points, neighbors_per_curve, new_normals_su, new_lengths_cu); | ||||
| } | } | ||||
| else { | else { | ||||
| ▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Lines | threading::parallel_for(normals_su.index_range(), 256, [&](const IndexRange range) { | ||||
| normals_su[i] = this->compute_point_normal_su(looptri_index, bary_coord); | normals_su[i] = this->compute_point_normal_su(looptri_index, bary_coord); | ||||
| } | } | ||||
| }); | }); | ||||
| return normals_su; | return normals_su; | ||||
| } | } | ||||
| void initialize_surface_attachment(const AddedPoints &added_points) | void initialize_surface_attachment(const AddedPoints &added_points) | ||||
| { | { | ||||
| MutableSpan<int> surface_triangle_indices = curves_->surface_triangle_indices_for_write(); | MutableSpan<float2> surface_uv_coords = curves_->surface_uv_coords_for_write(); | ||||
| MutableSpan<float2> surface_triangle_coords = curves_->surface_triangle_coords_for_write(); | |||||
| threading::parallel_for( | threading::parallel_for( | ||||
| added_points.bary_coords.index_range(), 1024, [&](const IndexRange range) { | added_points.bary_coords.index_range(), 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; | ||||
| surface_triangle_indices[curve_i] = added_points.looptri_indices[i]; | const MLoopTri &looptri = surface_looptris_[added_points.looptri_indices[i]]; | ||||
| surface_triangle_coords[curve_i] = float2(added_points.bary_coords[i]); | const float2 &uv0 = surface_uv_map_[looptri.tri[0]]; | ||||
| const float2 &uv1 = surface_uv_map_[looptri.tri[1]]; | |||||
| const float2 &uv2 = surface_uv_map_[looptri.tri[2]]; | |||||
| const float3 &bary_coords = added_points.bary_coords[i]; | |||||
| const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2); | |||||
| surface_uv_coords[curve_i] = uv; | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| /** | /** | ||||
| * 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, | ||||
| Show All 21 Lines | struct AddOperationExecutor { | ||||
| * 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 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 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]; | ||||
| 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(root_cu, tip_cu, positions_cu.slice(points)); | initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); | ||||
| continue; | continue; | ||||
| } | } | ||||
| positions_cu.slice(points).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 float3 &neighbor_first_pos_cu = | ||||
| positions_cu[curves_->points_for_curve(neighbor_curve_i).first()]; | |||||
HooglyBoogly: It would be a bit simpler to use `curves_->offsets()[neighbor_curve_i]` rather than `.first()`… | |||||
| const float3 neighbor_first_pos_su = curves_to_surface_mat_ * neighbor_first_pos_cu; | |||||
| BVHTreeNearest nearest; | |||||
| nearest.dist_sq = FLT_MAX; | |||||
| BLI_bvhtree_find_nearest(surface_bvh_.tree, | |||||
| neighbor_first_pos_su, | |||||
| &nearest, | |||||
| surface_bvh_.nearest_callback, | |||||
| &surface_bvh_); | |||||
| const int neighbor_looptri_index = nearest.index; | |||||
| const MLoopTri &neighbor_looptri = surface_looptris_[neighbor_looptri_index]; | |||||
| float3 neighbor_bary_coord{surface_triangle_coords[neighbor_curve_i]}; | const float3 neighbor_bary_coord = this->get_bary_coords( | ||||
| neighbor_bary_coord.z = 1.0f - neighbor_bary_coord.x - neighbor_bary_coord.y; | *surface_, neighbor_looptri, nearest.co); | ||||
| const float3 neighbor_normal_su = this->compute_point_normal_su( | const float3 neighbor_normal_su = this->compute_point_normal_su( | ||||
| neighbor_looptri_index, neighbor_bary_coord); | neighbor_looptri_index, neighbor_bary_coord); | ||||
| const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat_ * | const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat_ * | ||||
| neighbor_normal_su); | neighbor_normal_su); | ||||
| /* The rotation matrix used to transform relative coordinates of the neighbor curve | /* The rotation matrix used to transform relative coordinates of the neighbor curve | ||||
| * to the new curve. */ | * to the new curve. */ | ||||
| ▲ Show 20 Lines • Show All 58 Lines • Show Last 20 Lines | |||||
It would be a bit simpler to use curves_->offsets()[neighbor_curve_i] rather than .first(), but I'm fine with this too.