Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2022-09-18 22:56:15 +0300
committerHans Goudey <h.goudey@me.com>2022-09-18 22:56:24 +0300
commit7536abbe16bd8672ca38e2b866f9a9585d054713 (patch)
tree0989cabe13fe970eb8b14cdf84ea943bf6aba746 /source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
parentcf56b8be37c071f7d7fa22e3ab84029f4c895863 (diff)
Curves: Port Curve to Points node to the new data-block
This is the last node to use the `CurveEval` type. Since the curve to points node is basically the same as the resample node, now it just reuses the resample code and moves the curve point `CustomData` to a new point cloud at the end. I had to add support for sampling tangents and normals to the resampling. There is one behavior change: If the radius attribute doesn't exist, the node won't set the radius to 1 for the output point cloud anymore. Instead, the default radius for point clouds will be used. That issue was similar to T99814. Differential Revision: https://developer.blender.org/D16008
Diffstat (limited to 'source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc')
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc396
1 files changed, 107 insertions, 289 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
index 0ddf06dc8c7..c32600eccd1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -4,9 +4,13 @@
#include "BLI_task.hh"
#include "BLI_timeit.hh"
+#include "DNA_pointcloud_types.h"
+
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
+#include "GEO_resample_curves.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -62,9 +66,9 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
}
-static void curve_create_default_rotation_attribute(Span<float3> tangents,
- Span<float3> normals,
- MutableSpan<float3> rotations)
+static void fill_rotation_attribute(const Span<float3> tangents,
+ const Span<float3> normals,
+ MutableSpan<float3> rotations)
{
threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) {
for (const int i : range) {
@@ -74,239 +78,30 @@ static void curve_create_default_rotation_attribute(Span<float3> tangents,
});
}
-static Array<int> calculate_spline_point_offsets(GeoNodeExecParams &params,
- const GeometryNodeCurveResampleMode mode,
- const CurveEval &curve,
- const Span<SplinePtr> splines)
-{
- const int size = curve.splines().size();
- switch (mode) {
- case GEO_NODE_CURVE_RESAMPLE_COUNT: {
- const int count = params.get_input<int>("Count");
- if (count < 1) {
- return {0};
- }
- Array<int> offsets(size + 1);
- int offset = 0;
- for (const int i : IndexRange(size)) {
- offsets[i] = offset;
- if (splines[i]->evaluated_points_num() > 0) {
- offset += count;
- }
- }
- offsets.last() = offset;
- return offsets;
- }
- case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
- /* Don't allow asymptotic count increase for low resolution values. */
- const float resolution = std::max(params.get_input<float>("Length"), 0.0001f);
- Array<int> offsets(size + 1);
- int offset = 0;
- for (const int i : IndexRange(size)) {
- offsets[i] = offset;
- if (splines[i]->evaluated_points_num() > 0) {
- offset += splines[i]->length() / resolution + 1;
- }
- }
- offsets.last() = offset;
- return offsets;
- }
- case GEO_NODE_CURVE_RESAMPLE_EVALUATED: {
- return curve.evaluated_point_offsets();
- }
- }
- BLI_assert_unreachable();
- return {0};
-}
-
-/**
- * \note Relies on the fact that all attributes on point clouds are stored contiguously.
- */
-static GMutableSpan ensure_point_attribute(PointCloudComponent &points,
- const AttributeIDRef &attribute_id,
- const eCustomDataType data_type)
-{
- GAttributeWriter attribute = points.attributes_for_write()->lookup_or_add_for_write(
- attribute_id, ATTR_DOMAIN_POINT, data_type);
- GMutableSpan span = attribute.varray.get_internal_span();
- attribute.finish();
- return span;
-}
-
-template<typename T>
-static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points,
- const AttributeIDRef &attribute_id)
-{
- AttributeWriter<T> attribute = points.attributes_for_write()->lookup_or_add_for_write<T>(
- attribute_id, ATTR_DOMAIN_POINT);
- MutableSpan<T> span = attribute.varray.get_internal_span();
- attribute.finish();
- return span;
-}
-
-namespace {
-struct AnonymousAttributeIDs {
- StrongAnonymousAttributeID tangent_id;
- StrongAnonymousAttributeID normal_id;
- StrongAnonymousAttributeID rotation_id;
-};
-
-struct ResultAttributes {
- MutableSpan<float3> positions;
- MutableSpan<float> radii;
-
- Map<AttributeIDRef, GMutableSpan> point_attributes;
-
- MutableSpan<float3> tangents;
- MutableSpan<float3> normals;
- MutableSpan<float3> rotations;
-};
-} // namespace
-
-static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points,
- const CurveEval &curve,
- const AnonymousAttributeIDs &attributes)
+static PointCloud *pointcloud_from_curves(bke::CurvesGeometry curves,
+ const AttributeIDRef &tangent_id,
+ const AttributeIDRef &normal_id,
+ const AttributeIDRef &rotation_id)
{
- ResultAttributes outputs;
-
- outputs.positions = ensure_point_attribute<float3>(points, "position");
- outputs.radii = ensure_point_attribute<float>(points, "radius");
-
- if (attributes.tangent_id) {
- outputs.tangents = ensure_point_attribute<float3>(points, attributes.tangent_id.get());
- }
- if (attributes.normal_id) {
- outputs.normals = ensure_point_attribute<float3>(points, attributes.normal_id.get());
- }
- if (attributes.rotation_id) {
- outputs.rotations = ensure_point_attribute<float3>(points, attributes.rotation_id.get());
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(0);
+ pointcloud->totpoint = curves.points_num();
+
+ if (rotation_id) {
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
+ const VArraySpan<float3> tangents = attributes.lookup<float3>(tangent_id, ATTR_DOMAIN_POINT);
+ const VArraySpan<float3> normals = attributes.lookup<float3>(normal_id, ATTR_DOMAIN_POINT);
+ SpanAttributeWriter<float3> rotations = attributes.lookup_or_add_for_write_only_span<float3>(
+ rotation_id, ATTR_DOMAIN_POINT);
+ fill_rotation_attribute(tangents, normals, rotations.span);
+ rotations.finish();
}
- /* Because of the invariants of the curve component, we use the attributes of the first spline
- * as a representative for the attribute meta data all splines. Attributes from the spline domain
- * are handled separately. */
- curve.splines().first()->attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
- if (id.should_be_kept()) {
- outputs.point_attributes.add_new(
- id, ensure_point_attribute(points, id, meta_data.data_type));
- }
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- return outputs;
-}
-
-/**
- * TODO: For non-poly splines, this has double copies that could be avoided as part
- * of a general look at optimizing uses of #Spline::interpolate_to_evaluated.
- */
-static void copy_evaluated_point_attributes(const Span<SplinePtr> splines,
- const Span<int> offsets,
- ResultAttributes &data)
-{
- threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
- for (const int i : range) {
- const Spline &spline = *splines[i];
- const int offset = offsets[i];
- const int size = offsets[i + 1] - offsets[i];
-
- data.positions.slice(offset, size).copy_from(spline.evaluated_positions());
- spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size));
+ /* Move the curve point custom data to the pointcloud, to avoid any copying. */
+ CustomData_free(&pointcloud->pdata, pointcloud->totpoint);
+ pointcloud->pdata = curves.point_data;
+ CustomData_reset(&curves.point_data);
- for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
- const AttributeIDRef attribute_id = item.key;
- const GMutableSpan dst = item.value;
-
- BLI_assert(spline.attributes.get_for_read(attribute_id));
- GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
-
- spline.interpolate_to_evaluated(spline_span).materialize(dst.slice(offset, size).data());
- }
-
- if (!data.tangents.is_empty()) {
- data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents());
- }
- if (!data.normals.is_empty()) {
- data.normals.slice(offset, size).copy_from(spline.evaluated_normals());
- }
- }
- });
-}
-
-static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines,
- const Span<int> offsets,
- ResultAttributes &data)
-{
- threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
- for (const int i : range) {
- const Spline &spline = *splines[i];
- const int offset = offsets[i];
- const int num = offsets[i + 1] - offsets[i];
- if (num == 0) {
- continue;
- }
-
- const Array<float> uniform_samples = spline.sample_uniform_index_factors(num);
-
- spline.sample_with_index_factors<float3>(
- spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num));
- spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()),
- uniform_samples,
- data.radii.slice(offset, num));
-
- for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
- const AttributeIDRef attribute_id = item.key;
- const GMutableSpan dst = item.value;
-
- BLI_assert(spline.attributes.get_for_read(attribute_id));
- GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
-
- spline.sample_with_index_factors(
- spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num));
- }
-
- if (!data.tangents.is_empty()) {
- Span<float3> src_tangents = spline.evaluated_tangents();
- MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num);
- spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents);
- for (float3 &vector : sampled_tangents) {
- vector = math::normalize(vector);
- }
- }
-
- if (!data.normals.is_empty()) {
- Span<float3> src_normals = spline.evaluated_normals();
- MutableSpan<float3> sampled_normals = data.normals.slice(offset, num);
- spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals);
- for (float3 &vector : sampled_normals) {
- vector = math::normalize(vector);
- }
- }
- }
- });
-}
-
-static void copy_spline_domain_attributes(const CurveEval &curve,
- const Span<int> offsets,
- PointCloudComponent &points)
-{
- curve.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id);
- const CPPType &type = curve_attribute.type();
- const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type);
-
- for (const int i : curve.splines().index_range()) {
- const int offset = offsets[i];
- const int num = offsets[i + 1] - offsets[i];
- type.fill_assign_n(curve_attribute[i], dst[offset], num);
- }
-
- return true;
- },
- ATTR_DOMAIN_CURVE);
+ return pointcloud;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -315,73 +110,96 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- AnonymousAttributeIDs attribute_outputs;
- attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent");
- attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal");
- attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
-
GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
- geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- geometry_set.remove_geometry_during_modify();
- return;
- }
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *geometry_set.get_curves_for_read());
- const Span<SplinePtr> splines = curve->splines();
- curve->assert_valid_point_attributes();
-
- const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines);
- const int total_num = offsets.last();
- if (total_num == 0) {
- geometry_set.remove_geometry_during_modify();
- return;
- }
+ StrongAnonymousAttributeID tangent_anonymous_id;
+ StrongAnonymousAttributeID normal_anonymous_id;
+ StrongAnonymousAttributeID rotation_anonymous_id;
+ const bool rotation_required = params.output_is_required("Rotation");
+ if (params.output_is_required("Tangent") || rotation_required) {
+ tangent_anonymous_id = StrongAnonymousAttributeID("Tangent");
+ }
+ if (params.output_is_required("Normal") || rotation_required) {
+ normal_anonymous_id = StrongAnonymousAttributeID("Normal");
+ }
+ if (rotation_required) {
+ rotation_anonymous_id = StrongAnonymousAttributeID("Rotation");
+ }
- geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num));
- PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
- ResultAttributes point_attributes = create_attributes_for_transfer(
- points, *curve, attribute_outputs);
+ geometry::ResampleCurvesOutputAttributeIDs resample_attributes;
+ resample_attributes.tangent_id = tangent_anonymous_id.get();
+ resample_attributes.normal_id = normal_anonymous_id.get();
- switch (mode) {
- case GEO_NODE_CURVE_RESAMPLE_COUNT:
- case GEO_NODE_CURVE_RESAMPLE_LENGTH:
- copy_uniform_sample_point_attributes(splines, offsets, point_attributes);
- break;
- case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
- copy_evaluated_point_attributes(splines, offsets, point_attributes);
- break;
+ switch (mode) {
+ case GEO_NODE_CURVE_RESAMPLE_COUNT: {
+ Field<int> count = params.extract_input<Field<int>>("Count");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_count(
+ src_curves, fn::make_constant_field<bool>(true), count, resample_attributes);
+ PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
+ resample_attributes.tangent_id,
+ resample_attributes.normal_id,
+ rotation_anonymous_id.get());
+ geometry.remove_geometry_during_modify();
+ geometry.replace_pointcloud(pointcloud);
+ }
+ });
+ break;
}
-
- copy_spline_domain_attributes(*curve, offsets, points);
-
- if (!point_attributes.rotations.is_empty()) {
- curve_create_default_rotation_attribute(
- point_attributes.tangents, point_attributes.normals, point_attributes.rotations);
+ case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
+ Field<float> length = params.extract_input<Field<float>>("Length");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_length(
+ src_curves, fn::make_constant_field<bool>(true), length, resample_attributes);
+ PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
+ resample_attributes.tangent_id,
+ resample_attributes.normal_id,
+ rotation_anonymous_id.get());
+ geometry.remove_geometry_during_modify();
+ geometry.replace_pointcloud(pointcloud);
+ }
+ });
+ break;
}
-
- geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
- });
+ case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(
+ src_curves, fn::make_constant_field<bool>(true), resample_attributes);
+ PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
+ resample_attributes.tangent_id,
+ resample_attributes.normal_id,
+ rotation_anonymous_id.get());
+ geometry.remove_geometry_during_modify();
+ geometry.replace_pointcloud(pointcloud);
+ }
+ });
+ break;
+ }
params.set_output("Points", std::move(geometry_set));
- if (attribute_outputs.tangent_id) {
- params.set_output(
- "Tangent",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id),
- params.attribute_producer_name()));
+ if (tangent_anonymous_id) {
+ params.set_output("Tangent",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(tangent_anonymous_id), params.attribute_producer_name()));
}
- if (attribute_outputs.normal_id) {
- params.set_output(
- "Normal",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id),
- params.attribute_producer_name()));
+ if (normal_anonymous_id) {
+ params.set_output("Normal",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(normal_anonymous_id), params.attribute_producer_name()));
}
- if (attribute_outputs.rotation_id) {
- params.set_output(
- "Rotation",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id),
- params.attribute_producer_name()));
+ if (rotation_anonymous_id) {
+ params.set_output("Rotation",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(rotation_anonymous_id), params.attribute_producer_name()));
}
}