Changeset View
Changeset View
Standalone View
Standalone View
source/blender/geometry/intern/add_curves_on_mesh.cc
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
| #include "BLI_length_parameterize.hh" | |||||
| #include "BKE_attribute_math.hh" | |||||
| #include "BKE_mesh_sample.hh" | #include "BKE_mesh_sample.hh" | ||||
| #include "BKE_spline.hh" | |||||
| #include "GEO_add_curves_on_mesh.hh" | #include "GEO_add_curves_on_mesh.hh" | ||||
| /** | /** | ||||
| * The code below uses a suffix naming convention to indicate the coordinate space: | * The code below uses a suffix 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. | ||||
| */ | */ | ||||
| namespace blender::geometry { | namespace blender::geometry { | ||||
| namespace lp = length_parameterize; | |||||
| using bke::CurvesGeometry; | using bke::CurvesGeometry; | ||||
| struct NeighborCurve { | struct NeighborCurve { | ||||
| /* 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; | ||||
| }; | }; | ||||
| ▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | static void interpolate_position_with_interpolation(CurvesGeometry &curves, | ||||
| const Span<MLoopTri> surface_looptris, | const Span<MLoopTri> surface_looptris, | ||||
| const Mesh &surface, | const Mesh &surface, | ||||
| const Span<float3> corner_normals_su) | const Span<float3> corner_normals_su) | ||||
| { | { | ||||
| MutableSpan<float3> positions_cu = curves.positions_for_write(); | MutableSpan<float3> positions_cu = curves.positions_for_write(); | ||||
| const int added_curves_num = root_positions_cu.size(); | const int added_curves_num = root_positions_cu.size(); | ||||
| threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) { | threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) { | ||||
| for (const int i : range) { | for (const int added_curve_i : range) { | ||||
| const NeighborCurves &neighbors = neighbors_per_curve[i]; | const NeighborCurves &neighbors = neighbors_per_curve[added_curve_i]; | ||||
| const int curve_i = old_curves_num + i; | const int curve_i = old_curves_num + added_curve_i; | ||||
| const IndexRange points = curves.points_for_curve(curve_i); | const IndexRange points = curves.points_for_curve(curve_i); | ||||
| const float length_cu = new_lengths_cu[i]; | const float length_cu = new_lengths_cu[added_curve_i]; | ||||
| const float3 &normal_su = new_normals_su[i]; | const float3 &normal_su = new_normals_su[added_curve_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 = root_positions_cu[i]; | const float3 &root_cu = root_positions_cu[added_curve_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; | ||||
| } | } | ||||
| Show All 26 Lines | for (const int added_curve_i : range) { | ||||
| /* 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. */ | ||||
| float normal_rotation_cu[3][3]; | float normal_rotation_cu[3][3]; | ||||
| rotation_between_vecs_to_mat3(normal_rotation_cu, neighbor_normal_cu, normal_cu); | rotation_between_vecs_to_mat3(normal_rotation_cu, neighbor_normal_cu, normal_cu); | ||||
| const IndexRange neighbor_points = curves.points_for_curve(neighbor_curve_i); | const IndexRange neighbor_points = curves.points_for_curve(neighbor_curve_i); | ||||
| const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]]; | const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]]; | ||||
| /* Use a temporary #PolySpline, because that's the easiest way to resample an | /* Sample the positions on neighbors and mix them into the final positions of the curve. | ||||
| * existing curve right now. Resampling is necessary if the length of the new curve | * Resampling is necessary if the length of the new curve does not match the length of the | ||||
| * does not match the length of the neighbors or the number of handle points is | * neighbors or the number of handle points is different. | ||||
| * different. */ | * | ||||
| PolySpline neighbor_spline; | * TODO: The lengths can be cached so they aren't recomputed if a curve is a neighbor for | ||||
| neighbor_spline.resize(neighbor_points.size()); | * multiple new curves. Also, allocations could be avoided by reusing some arrays. */ | ||||
| neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points)); | |||||
| neighbor_spline.mark_cache_invalid(); | const Span<float3> neighbor_positions_cu = positions_cu.slice(neighbor_points); | ||||
| Array<float> lengths(lp::lengths_num(neighbor_points.size(), false)); | |||||
JacquesLucke: Could use some bigger inline buffer to avoid an allocation every time. | |||||
| lp::accumulate_lengths<float3>(neighbor_positions_cu, false, lengths); | |||||
| const float neighbor_length_cu = lengths.last(); | |||||
| const float neighbor_length_cu = neighbor_spline.length(); | Array<float> sample_lengths(points.size()); | ||||
| 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.size() - 1.0f)) * length_factor; | const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor; | ||||
| for (const int j : IndexRange(points.size())) { | for (const int i : sample_lengths.index_range()) { | ||||
| const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( | sample_lengths[i] = i * resample_factor; | ||||
| j * resample_factor); | } | ||||
| const float index_factor = lookup.evaluated_index + lookup.factor; | |||||
| float3 p; | Array<int> indices(points.size()); | ||||
| neighbor_spline.sample_with_index_factors<float3>( | Array<float> factors(points.size()); | ||||
| neighbor_spline.positions(), {&index_factor, 1}, {&p, 1}); | lp::create_samples_from_sorted_lengths(lengths, sample_lengths, false, indices, factors); | ||||
| const float3 relative_coord = p - neighbor_root_cu; | |||||
| float3 rotated_relative_coord = relative_coord; | for (const int i : IndexRange(points.size())) { | ||||
| const float3 sample_cu = math::interpolate(neighbor_positions_cu[indices[i]], | |||||
| neighbor_positions_cu[indices[i] + 1], | |||||
| factors[i]); | |||||
| const float3 relative_to_root_cu = sample_cu - neighbor_root_cu; | |||||
| float3 rotated_relative_coord = relative_to_root_cu; | |||||
| mul_m3_v3(normal_rotation_cu, rotated_relative_coord); | mul_m3_v3(normal_rotation_cu, rotated_relative_coord); | ||||
| positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; | positions_cu[points[i]] += neighbor.weight * rotated_relative_coord; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inputs) | void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inputs) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 123 Lines • Show Last 20 Lines | |||||
Could use some bigger inline buffer to avoid an allocation every time.