diff options
Diffstat (limited to 'source/blender/geometry/intern/add_curves_on_mesh.cc')
-rw-r--r-- | source/blender/geometry/intern/add_curves_on_mesh.cc | 216 |
1 files changed, 131 insertions, 85 deletions
diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index 34551bd474f..bb5e2a0a28a 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -1,9 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_length_parameterize.hh" +#include "BLI_task.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" #include "BKE_mesh_sample.hh" -#include "BKE_spline.hh" #include "GEO_add_curves_on_mesh.hh" +#include "GEO_reverse_uv_sampler.hh" /** * The code below uses a suffix naming convention to indicate the coordinate space: @@ -25,9 +30,9 @@ struct NeighborCurve { static constexpr int max_neighbors = 5; using NeighborCurves = Vector<NeighborCurve, max_neighbors>; -static float3 compute_surface_point_normal(const MLoopTri &looptri, - const float3 &bary_coord, - const Span<float3> corner_normals) +float3 compute_surface_point_normal(const MLoopTri &looptri, + const float3 &bary_coord, + const Span<float3> corner_normals) { const int l0 = looptri.tri[0]; const int l1 = looptri.tri[1]; @@ -99,8 +104,8 @@ void interpolate_from_neighbors(const Span<NeighborCurves> neighbors_per_curve, } } } + mixer.finalize(range); }); - mixer.finalize(); } static void interpolate_position_without_interpolation( @@ -134,27 +139,26 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, const int old_curves_num, const Span<float> new_lengths_cu, const Span<float3> new_normals_su, - const float4x4 &surface_to_curves_normal_mat, - const float4x4 &curves_to_surface_mat, - const BVHTreeFromMesh &surface_bvh, - const Span<MLoopTri> surface_looptris, - const Mesh &surface, + const bke::CurvesSurfaceTransforms &transforms, + const ReverseUVSampler &reverse_uv_sampler, const Span<float3> corner_normals_su) { MutableSpan<float3> positions_cu = curves.positions_for_write(); const int added_curves_num = root_positions_cu.size(); + const Span<float2> uv_coords = curves.surface_uv_coords(); + threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) { - for (const int i : range) { - const NeighborCurves &neighbors = neighbors_per_curve[i]; - const int curve_i = old_curves_num + i; + for (const int added_curve_i : range) { + const NeighborCurves &neighbors = neighbors_per_curve[added_curve_i]; + const int curve_i = old_curves_num + added_curve_i; const IndexRange points = curves.points_for_curve(curve_i); - const float length_cu = new_lengths_cu[i]; - const float3 &normal_su = new_normals_su[i]; - const float3 normal_cu = math::normalize(surface_to_curves_normal_mat * normal_su); + const float length_cu = new_lengths_cu[added_curve_i]; + const float3 &normal_su = new_normals_su[added_curve_i]; + const float3 normal_cu = math::normalize(transforms.surface_to_curves_normal * 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 there are no neighbors, just make a straight line. */ @@ -167,26 +171,15 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, for (const NeighborCurve &neighbor : neighbors) { const int neighbor_curve_i = neighbor.index; - const float3 &neighbor_first_pos_cu = positions_cu[curves.offsets()[neighbor_curve_i]]; - 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, - const_cast<BVHTreeFromMesh *>(&surface_bvh)); - const int neighbor_looptri_index = nearest.index; - const MLoopTri &neighbor_looptri = surface_looptris[neighbor_looptri_index]; - - const float3 neighbor_bary_coord = - bke::mesh_surface_sample::compute_bary_coord_in_triangle( - surface, neighbor_looptri, nearest.co); + const float2 neighbor_uv = uv_coords[neighbor_curve_i]; + const ReverseUVSampler::Result result = reverse_uv_sampler.sample(neighbor_uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + continue; + } const float3 neighbor_normal_su = compute_surface_point_normal( - surface_looptris[neighbor_looptri_index], neighbor_bary_coord, corner_normals_su); - const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat * + *result.looptri, result.bary_weights, corner_normals_su); + const float3 neighbor_normal_cu = math::normalize(transforms.surface_to_curves_normal * neighbor_normal_su); /* The rotation matrix used to transform relative coordinates of the neighbor curve @@ -197,54 +190,96 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, const IndexRange neighbor_points = curves.points_for_curve(neighbor_curve_i); const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]]; - /* Use a temporary #PolySpline, because that's the easiest way to resample an - * existing curve right now. Resampling is necessary if the length of the new curve - * does not match the length of the neighbors or the number of handle points is - * different. */ - PolySpline neighbor_spline; - neighbor_spline.resize(neighbor_points.size()); - neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points)); - neighbor_spline.mark_cache_invalid(); + /* Sample the positions on neighbors and mix them into the final positions of the curve. + * Resampling is necessary if the length of the new curve does not match the length of the + * neighbors or the number of handle points is different. + * + * TODO: The lengths can be cached so they aren't recomputed if a curve is a neighbor for + * multiple new curves. Also, allocations could be avoided by reusing some arrays. */ + + const Span<float3> neighbor_positions_cu = positions_cu.slice(neighbor_points); + if (neighbor_positions_cu.size() == 1) { + /* Skip interpolating positions from neighbors with only one point. */ + continue; + } + Array<float, 32> lengths(length_parameterize::segments_num(neighbor_points.size(), false)); + length_parameterize::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, 32> sample_lengths(points.size()); 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; - for (const int j : IndexRange(points.size())) { - const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( - j * resample_factor); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - neighbor_spline.sample_with_index_factors<float3>( - neighbor_spline.positions(), {&index_factor, 1}, {&p, 1}); - const float3 relative_coord = p - neighbor_root_cu; - float3 rotated_relative_coord = relative_coord; + for (const int i : sample_lengths.index_range()) { + sample_lengths[i] = i * resample_factor * neighbor_length_cu; + } + + Array<int, 32> indices(points.size()); + Array<float, 32> factors(points.size()); + length_parameterize::sample_at_lengths(lengths, sample_lengths, indices, factors); + + 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); - 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) +AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves, + const AddCurvesOnMeshInputs &inputs) { + AddCurvesOnMeshOutputs outputs; + const bool use_interpolation = inputs.interpolate_length || inputs.interpolate_point_count || inputs.interpolate_shape; + Vector<float3> root_positions_cu; + Vector<float3> bary_coords; + Vector<const MLoopTri *> looptris; + Vector<float2> used_uvs; + + /* Find faces that the passed in uvs belong to. */ + const Span<MVert> surface_verts = inputs.surface->verts(); + const Span<MLoop> surface_loops = inputs.surface->loops(); + for (const int i : inputs.uvs.index_range()) { + const float2 &uv = inputs.uvs[i]; + const ReverseUVSampler::Result result = inputs.reverse_uv_sampler->sample(uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + outputs.uv_error = true; + continue; + } + const MLoopTri &looptri = *result.looptri; + bary_coords.append(result.bary_weights); + looptris.append(&looptri); + const float3 root_position_su = attribute_math::mix3<float3>( + result.bary_weights, + surface_verts[surface_loops[looptri.tri[0]].v].co, + surface_verts[surface_loops[looptri.tri[1]].v].co, + surface_verts[surface_loops[looptri.tri[2]].v].co); + root_positions_cu.append(inputs.transforms->surface_to_curves * root_position_su); + used_uvs.append(uv); + } + Array<NeighborCurves> neighbors_per_curve; if (use_interpolation) { BLI_assert(inputs.old_roots_kdtree != nullptr); - neighbors_per_curve = find_curve_neighbors(inputs.root_positions_cu, *inputs.old_roots_kdtree); + neighbors_per_curve = find_curve_neighbors(root_positions_cu, *inputs.old_roots_kdtree); } - const int added_curves_num = inputs.root_positions_cu.size(); + const int added_curves_num = root_positions_cu.size(); const int old_points_num = curves.points_num(); const int old_curves_num = curves.curves_num(); const int new_curves_num = old_curves_num + added_curves_num; /* Grow number of curves first, so that the offsets array can be filled. */ curves.resize(old_points_num, new_curves_num); + const IndexRange new_curves_range = curves.curves_range().drop_front(old_curves_num); /* Compute new curve offsets. */ MutableSpan<int> curve_offsets = curves.offsets_for_write(); @@ -259,14 +294,18 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp else { new_point_counts_per_curve.fill(inputs.fallback_point_count); } - for (const int i : IndexRange(added_curves_num)) { - curve_offsets[old_curves_num + i + 1] += curve_offsets[old_curves_num + i]; + for (const int i : new_curves_range) { + curve_offsets[i + 1] += curve_offsets[i]; } const int new_points_num = curves.offsets().last(); curves.resize(new_points_num, new_curves_num); MutableSpan<float3> positions_cu = curves.positions_for_write(); + /* Initialize attachment information. */ + MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write(); + surface_uv_coords.take_back(added_curves_num).copy_from(used_uvs); + /* Determine length of new curves. */ Array<float> new_lengths_cu(added_curves_num); if (inputs.interpolate_length) { @@ -293,25 +332,11 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp Array<float3> new_normals_su(added_curves_num); threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) { for (const int i : range) { - const int looptri_index = inputs.looptri_indices[i]; - const float3 &bary_coord = inputs.bary_coords[i]; new_normals_su[i] = compute_surface_point_normal( - inputs.surface_looptris[looptri_index], bary_coord, inputs.corner_normals_su); + *looptris[i], bary_coords[i], inputs.corner_normals_su); } }); - /* Propagate attachment information. */ - if (!inputs.surface_uv_map.is_empty()) { - MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write(); - bke::mesh_surface_sample::sample_corner_attribute( - *inputs.surface, - inputs.looptri_indices, - inputs.bary_coords, - GVArray::ForSpan(inputs.surface_uv_map), - IndexRange(added_curves_num), - surface_uv_coords.take_back(added_curves_num)); - } - /* Update selection arrays when available. */ const VArray<float> points_selection = curves.selection_point_float(); if (points_selection.is_span()) { @@ -321,34 +346,55 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp const VArray<float> curves_selection = curves.selection_curve_float(); if (curves_selection.is_span()) { MutableSpan<float> curves_selection_span = curves.selection_curve_float_for_write(); - curves_selection_span.drop_front(old_curves_num).fill(1.0f); + curves_selection_span.slice(new_curves_range).fill(1.0f); } /* Initialize position attribute. */ if (inputs.interpolate_shape) { interpolate_position_with_interpolation(curves, - inputs.root_positions_cu, + root_positions_cu, neighbors_per_curve, old_curves_num, new_lengths_cu, new_normals_su, - inputs.surface_to_curves_normal_mat, - inputs.curves_to_surface_mat, - *inputs.surface_bvh, - inputs.surface_looptris, - *inputs.surface, + *inputs.transforms, + *inputs.reverse_uv_sampler, inputs.corner_normals_su); } else { interpolate_position_without_interpolation(curves, old_curves_num, - inputs.root_positions_cu, + root_positions_cu, new_lengths_cu, new_normals_su, - inputs.surface_to_curves_normal_mat); + inputs.transforms->surface_to_curves_normal); } - curves.update_curve_types(); + curves.fill_curve_types(new_curves_range, CURVE_TYPE_CATMULL_ROM); + + /* Explicitly set all other attributes besides those processed above to default values. */ + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); + Set<std::string> attributes_to_skip{{"position", + "curve_type", + "surface_uv_coordinate", + ".selection_point_float", + ".selection_curve_float"}}; + attributes.for_all( + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) { + if (id.is_named() && attributes_to_skip.contains(id.name())) { + return true; + } + bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id); + const int new_elements_num = attribute.domain == ATTR_DOMAIN_POINT ? new_points_num : + new_curves_num; + const CPPType &type = attribute.span.type(); + GMutableSpan new_data = attribute.span.take_back(new_elements_num); + type.fill_assign_n(type.default_value(), new_data.data(), new_data.size()); + attribute.finish(); + return true; + }); + + return outputs; } } // namespace blender::geometry |