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>2021-09-16 20:25:26 +0300
committerHans Goudey <h.goudey@me.com>2021-09-16 20:25:26 +0300
commitf81bacd6f0fc8d9afb46570e1a66c4469185d8a5 (patch)
tree1f0a77380ca25d5a1de4549a3fd35353adc3ddf4
parentddb7cb7e4ab85d323fe23e4879196f4b1a23d4f5 (diff)
Geometry Nodes: Transfer attributes in the curve to mesh node
This patch allows point and spline attributes to be transferred to the mesh generated by the curve to mesh node. All dynamic named and anonymous attributes are transferred. So a user-created attribute will be transferred, but "radius", "tilt" or the handle position attributes won't be transferred by default and will need to be copied first. This trade-off is made for performance, since most of the time, users won't need these attributes copied. Generally, attributes are transferred to the point/vertex domain. However, if they have the same name as a built-in mesh attribute that only exists on a different domain, like "shade_smooth", then they can be transferred directly to that domain as well. Conversion directly to the face corner domain is not necessary because there are no builtin face corner attributes. I see this conversion directly to other domains as an optimization we could use behind the scenes in the future as well, when named attributes are less common. For performance, I haven't tested which of the following is better: ``` for each spline combination: for each attribute: for each attribute: for each spline combination: ``` For now I used the existing loop to avoid more threading overhead. Differential Revision: https://developer.blender.org/D12363
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc509
1 files changed, 452 insertions, 57 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index 2eae11d1705..f46440fd949 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -39,6 +39,20 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Mesh");
}
+/** Information about the creation of one curve spline and profile spline combination. */
+struct ResultInfo {
+ const Spline &spline;
+ const Spline &profile;
+ int vert_offset;
+ int edge_offset;
+ int loop_offset;
+ int poly_offset;
+ int spline_vert_len;
+ int spline_edge_len;
+ int profile_vert_len;
+ int profile_edge_len;
+};
+
static void vert_extrude_to_mesh_data(const Spline &spline,
const float3 profile_vert,
MutableSpan<MVert> r_verts,
@@ -75,44 +89,33 @@ static void mark_edges_sharp(MutableSpan<MEdge> edges)
}
}
-static void spline_extrude_to_mesh_data(const Spline &spline,
- const Spline &profile_spline,
- const int vert_offset,
- const int edge_offset,
- const int loop_offset,
- const int poly_offset,
+static void spline_extrude_to_mesh_data(const ResultInfo &info,
MutableSpan<MVert> r_verts,
MutableSpan<MEdge> r_edges,
MutableSpan<MLoop> r_loops,
MutableSpan<MPoly> r_polys)
{
- const int spline_vert_len = spline.evaluated_points_size();
- const int spline_edge_len = spline.evaluated_edges_size();
- const int profile_vert_len = profile_spline.evaluated_points_size();
- const int profile_edge_len = profile_spline.evaluated_edges_size();
- if (spline_vert_len == 0) {
- return;
- }
-
- if (profile_vert_len == 1) {
+ const Spline &spline = info.spline;
+ const Spline &profile = info.profile;
+ if (info.profile_vert_len == 1) {
vert_extrude_to_mesh_data(spline,
- profile_spline.evaluated_positions()[0],
+ profile.evaluated_positions()[0],
r_verts,
r_edges,
- vert_offset,
- edge_offset);
+ info.vert_offset,
+ info.edge_offset);
return;
}
/* Add the edges running along the length of the curve, starting at each profile vertex. */
- const int spline_edges_start = edge_offset;
- for (const int i_profile : IndexRange(profile_vert_len)) {
- const int profile_edge_offset = spline_edges_start + i_profile * spline_edge_len;
- for (const int i_ring : IndexRange(spline_edge_len)) {
- const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+ const int spline_edges_start = info.edge_offset;
+ for (const int i_profile : IndexRange(info.profile_vert_len)) {
+ const int profile_edge_offset = spline_edges_start + i_profile * info.spline_edge_len;
+ for (const int i_ring : IndexRange(info.spline_edge_len)) {
+ const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1;
- const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
- const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+ const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring;
+ const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring;
MEdge &edge = r_edges[profile_edge_offset + i_ring];
edge.v1 = ring_vert_offset + i_profile;
@@ -122,13 +125,14 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
}
/* Add the edges running along each profile ring. */
- const int profile_edges_start = spline_edges_start + profile_vert_len * spline_edge_len;
- for (const int i_ring : IndexRange(spline_vert_len)) {
- const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int profile_edges_start = spline_edges_start +
+ info.profile_vert_len * info.spline_edge_len;
+ for (const int i_ring : IndexRange(info.spline_vert_len)) {
+ const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring;
- const int ring_edge_offset = profile_edges_start + i_ring * profile_edge_len;
- for (const int i_profile : IndexRange(profile_edge_len)) {
- const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+ const int ring_edge_offset = profile_edges_start + i_ring * info.profile_edge_len;
+ for (const int i_profile : IndexRange(info.profile_edge_len)) {
+ const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1;
MEdge &edge = r_edges[ring_edge_offset + i_profile];
edge.v1 = ring_vert_offset + i_profile;
@@ -138,24 +142,25 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
}
/* Calculate poly and corner indices. */
- for (const int i_ring : IndexRange(spline_edge_len)) {
- const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+ for (const int i_ring : IndexRange(info.spline_edge_len)) {
+ const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1;
- const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
- const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+ const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring;
+ const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring;
- const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring;
- const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring;
+ const int ring_edge_start = profile_edges_start + info.profile_edge_len * i_ring;
+ const int next_ring_edge_offset = profile_edges_start + info.profile_edge_len * i_next_ring;
- const int ring_poly_offset = poly_offset + i_ring * profile_edge_len;
- const int ring_loop_offset = loop_offset + i_ring * profile_edge_len * 4;
+ const int ring_poly_offset = info.poly_offset + i_ring * info.profile_edge_len;
+ const int ring_loop_offset = info.loop_offset + i_ring * info.profile_edge_len * 4;
- for (const int i_profile : IndexRange(profile_edge_len)) {
+ for (const int i_profile : IndexRange(info.profile_edge_len)) {
const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4;
- const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+ const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1;
- const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile;
- const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile;
+ const int spline_edge_start = spline_edges_start + info.spline_edge_len * i_profile;
+ const int next_spline_edge_start = spline_edges_start +
+ info.spline_edge_len * i_next_profile;
MPoly &poly = r_polys[ring_poly_offset + i_profile];
poly.loopstart = ring_segment_loop_offset;
@@ -181,29 +186,30 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
Span<float3> positions = spline.evaluated_positions();
Span<float3> tangents = spline.evaluated_tangents();
Span<float3> normals = spline.evaluated_normals();
- Span<float3> profile_positions = profile_spline.evaluated_positions();
+ Span<float3> profile_positions = profile.evaluated_positions();
GVArray_Typed<float> radii = spline.interpolate_to_evaluated(spline.radii());
- for (const int i_ring : IndexRange(spline_vert_len)) {
+ for (const int i_ring : IndexRange(info.spline_vert_len)) {
float4x4 point_matrix = float4x4::from_normalized_axis_data(
positions[i_ring], normals[i_ring], tangents[i_ring]);
point_matrix.apply_scale(radii[i_ring]);
- const int ring_vert_start = vert_offset + i_ring * profile_vert_len;
- for (const int i_profile : IndexRange(profile_vert_len)) {
+ const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len;
+ for (const int i_profile : IndexRange(info.profile_vert_len)) {
MVert &vert = r_verts[ring_vert_start + i_profile];
copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
}
}
/* Mark edge loops from sharp vector control points sharp. */
- if (profile_spline.type() == Spline::Type::Bezier) {
- const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline);
+ if (profile.type() == Spline::Type::Bezier) {
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile);
Span<int> control_point_offsets = bezier_spline.control_point_offsets();
for (const int i : IndexRange(bezier_spline.size())) {
if (bezier_spline.point_is_sharp(i)) {
- mark_edges_sharp(r_edges.slice(
- spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len));
+ mark_edges_sharp(
+ r_edges.slice(spline_edges_start + info.spline_edge_len * control_point_offsets[i],
+ info.spline_edge_len));
}
}
}
@@ -272,6 +278,372 @@ static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<Spl
return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)};
}
+static AttributeDomain get_result_attribute_domain(const MeshComponent &component,
+ const AttributeIDRef &attribute_id)
+{
+ /* Only use a different domain if it is builtin and must only exist on one domain. */
+ if (!component.attribute_is_builtin(attribute_id)) {
+ return ATTR_DOMAIN_POINT;
+ }
+
+ std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(attribute_id);
+ if (!meta_data) {
+ /* This function has to return something in this case, but it shouldn't be used,
+ * so return an output that will assert later if the code attempts to handle it. */
+ return ATTR_DOMAIN_AUTO;
+ }
+
+ return meta_data->domain;
+}
+
+/**
+ * The data stored in the attribute and its domain from #OutputAttribute, to avoid calling
+ * `as_span()` for every single profile and curve spline combination, and for readability.
+ */
+struct ResultAttributeData {
+ GMutableSpan data;
+ AttributeDomain domain;
+};
+
+static std::optional<ResultAttributeData> create_attribute_and_get_span(
+ MeshComponent &component,
+ const AttributeIDRef &attribute_id,
+ AttributeMetaData meta_data,
+ Vector<OutputAttribute> &r_attributes)
+{
+ const AttributeDomain domain = get_result_attribute_domain(component, attribute_id);
+ OutputAttribute attribute = component.attribute_try_get_for_output_only(
+ attribute_id, domain, meta_data.data_type);
+ if (!attribute) {
+ return std::nullopt;
+ }
+
+ GMutableSpan span = attribute.as_span();
+ r_attributes.append(std::move(attribute));
+ return std::make_optional<ResultAttributeData>({span, domain});
+}
+
+/**
+ * Store the references to the attribute data from the curve and profile inputs. Here we rely on
+ * the invariants of the storage of curve attributes, that the order will be consistent between
+ * splines, and all splines will have the same attributes.
+ */
+struct ResultAttributes {
+ /**
+ * Result attributes on the mesh corresponding to each attribute on the curve input, in the same
+ * order. The data is optional only in case the attribute does not exist on the mesh for some
+ * reason, like "shade_smooth" when the result has no faces.
+ */
+ Vector<std::optional<ResultAttributeData>> curve_point_attributes;
+ Vector<std::optional<ResultAttributeData>> curve_spline_attributes;
+
+ /**
+ * Result attributes corresponding the attributes on the profile input, in the same order. The
+ * attributes are optional in case the attribute names correspond to a namse used by the curve
+ * input, in which case the curve input attributes take precedence.
+ */
+ Vector<std::optional<ResultAttributeData>> profile_point_attributes;
+ Vector<std::optional<ResultAttributeData>> profile_spline_attributes;
+
+ /**
+ * Because some builtin attributes are not stored contiguously, and the curve inputs might have
+ * attributes with those names, it's necessary to keep OutputAttributes around to give access to
+ * the result data in a contiguous array.
+ */
+ Vector<OutputAttribute> attributes;
+};
+static ResultAttributes create_result_attributes(const CurveEval &curve,
+ const CurveEval &profile,
+ Mesh &mesh)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(&mesh, GeometryOwnershipType::Editable);
+ Set<AttributeIDRef> curve_attributes;
+
+ /* In order to prefer attributes on the main curve input when there are name collisions, first
+ * check the attributes on the curve, then add attributes on the profile that are not also on the
+ * main curve input. */
+ ResultAttributes result;
+ curve.splines().first()->attributes.foreach_attribute(
+ [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
+ curve_attributes.add_new(id);
+ result.curve_point_attributes.append(
+ create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes));
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ curve.attributes.foreach_attribute(
+ [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
+ curve_attributes.add_new(id);
+ result.curve_spline_attributes.append(
+ create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes));
+ return true;
+ },
+ ATTR_DOMAIN_CURVE);
+ profile.splines().first()->attributes.foreach_attribute(
+ [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
+ if (curve_attributes.contains(id)) {
+ result.profile_point_attributes.append({});
+ }
+ else {
+ result.profile_point_attributes.append(
+ create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes));
+ }
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ profile.attributes.foreach_attribute(
+ [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
+ if (curve_attributes.contains(id)) {
+ result.profile_spline_attributes.append({});
+ }
+ else {
+ result.profile_spline_attributes.append(
+ create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes));
+ }
+ return true;
+ },
+ ATTR_DOMAIN_CURVE);
+
+ return result;
+}
+
+template<typename T>
+static void copy_curve_point_data_to_mesh_verts(const Span<T> src,
+ const ResultInfo &info,
+ MutableSpan<T> dst)
+{
+ for (const int i_ring : IndexRange(info.spline_vert_len)) {
+ const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len;
+ dst.slice(ring_vert_start, info.profile_vert_len).fill(src[i_ring]);
+ }
+}
+
+template<typename T>
+static void copy_curve_point_data_to_mesh_edges(const Span<T> src,
+ const ResultInfo &info,
+ MutableSpan<T> dst)
+{
+ const int edges_start = info.edge_offset + info.profile_vert_len * info.spline_edge_len;
+ for (const int i_ring : IndexRange(info.spline_vert_len)) {
+ const int ring_edge_start = edges_start + info.profile_edge_len * i_ring;
+ dst.slice(ring_edge_start, info.profile_edge_len).fill(src[i_ring]);
+ }
+}
+
+template<typename T>
+static void copy_curve_point_data_to_mesh_faces(const Span<T> src,
+ const ResultInfo &info,
+ MutableSpan<T> dst)
+{
+ for (const int i_ring : IndexRange(info.spline_edge_len)) {
+ const int ring_face_start = info.poly_offset + info.profile_edge_len * i_ring;
+ dst.slice(ring_face_start, info.profile_edge_len).fill(src[i_ring]);
+ }
+}
+
+static void copy_curve_point_attribute_to_mesh(const GSpan src,
+ const ResultInfo &info,
+ ResultAttributeData &dst)
+{
+ GVArrayPtr interpolated_gvarray = info.spline.interpolate_to_evaluated(src);
+ GSpan interpolated = interpolated_gvarray->get_internal_span();
+
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ switch (dst.domain) {
+ case ATTR_DOMAIN_POINT:
+ copy_curve_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>());
+ break;
+ case ATTR_DOMAIN_EDGE:
+ copy_curve_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>());
+ break;
+ case ATTR_DOMAIN_FACE:
+ copy_curve_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>());
+ break;
+ case ATTR_DOMAIN_CORNER:
+ /* Unsupported for now, since there are no builtin attributes to convert into. */
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ });
+}
+
+template<typename T>
+static void copy_profile_point_data_to_mesh_verts(const Span<T> src,
+ const ResultInfo &info,
+ MutableSpan<T> dst)
+{
+ for (const int i_ring : IndexRange(info.spline_vert_len)) {
+ const int profile_vert_start = info.vert_offset + i_ring * info.profile_vert_len;
+ for (const int i_profile : IndexRange(info.profile_vert_len)) {
+ dst[profile_vert_start + i_profile] = src[i_profile];
+ }
+ }
+}
+
+template<typename T>
+static void copy_profile_point_data_to_mesh_edges(const Span<T> src,
+ const ResultInfo &info,
+ MutableSpan<T> dst)
+{
+ for (const int i_profile : IndexRange(info.profile_vert_len)) {
+ const int profile_edge_offset = info.edge_offset + i_profile * info.spline_edge_len;
+ dst.slice(profile_edge_offset, info.spline_edge_len).fill(src[i_profile]);
+ }
+}
+
+template<typename T>
+static void copy_profile_point_data_to_mesh_faces(const Span<T> src,
+ const ResultInfo &info,
+ MutableSpan<T> dst)
+{
+ for (const int i_ring : IndexRange(info.spline_edge_len)) {
+ const int profile_face_start = info.poly_offset + i_ring * info.profile_edge_len;
+ for (const int i_profile : IndexRange(info.profile_edge_len)) {
+ dst[profile_face_start + i_profile] = src[i_profile];
+ }
+ }
+}
+
+static void copy_profile_point_attribute_to_mesh(const GSpan src,
+ const ResultInfo &info,
+ ResultAttributeData &dst)
+{
+ GVArrayPtr interpolated_gvarray = info.profile.interpolate_to_evaluated(src);
+ GSpan interpolated = interpolated_gvarray->get_internal_span();
+
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ switch (dst.domain) {
+ case ATTR_DOMAIN_POINT:
+ copy_profile_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>());
+ break;
+ case ATTR_DOMAIN_EDGE:
+ copy_profile_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>());
+ break;
+ case ATTR_DOMAIN_FACE:
+ copy_profile_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>());
+ break;
+ case ATTR_DOMAIN_CORNER:
+ /* Unsupported for now, since there are no builtin attributes to convert into. */
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ });
+}
+
+static void copy_point_domain_attributes_to_mesh(const ResultInfo &info,
+ ResultAttributes &attributes)
+{
+ if (!attributes.curve_point_attributes.is_empty()) {
+ int i = 0;
+ info.spline.attributes.foreach_attribute(
+ [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) {
+ if (attributes.curve_point_attributes[i]) {
+ copy_curve_point_attribute_to_mesh(*info.spline.attributes.get_for_read(id),
+ info,
+ *attributes.curve_point_attributes[i]);
+ }
+ i++;
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ }
+ if (!attributes.profile_point_attributes.is_empty()) {
+ int i = 0;
+ info.profile.attributes.foreach_attribute(
+ [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) {
+ if (attributes.profile_point_attributes[i]) {
+ copy_profile_point_attribute_to_mesh(*info.profile.attributes.get_for_read(id),
+ info,
+ *attributes.profile_point_attributes[i]);
+ }
+ i++;
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ }
+}
+
+template<typename T>
+static void copy_spline_data_to_mesh(Span<T> src, Span<int> offsets, MutableSpan<T> dst)
+{
+ for (const int i : IndexRange(src.size())) {
+ dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]);
+ }
+}
+
+/**
+ * Since the offsets for each combination of curve and profile spline are stored for every mesh
+ * domain, and this just needs to fill the chunks corresponding to each combination, we can use
+ * the same function for all mesh domains.
+ */
+static void copy_spline_attribute_to_mesh(const GSpan src,
+ const ResultOffsets &offsets,
+ ResultAttributeData &dst_attribute)
+{
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ switch (dst_attribute.domain) {
+ case ATTR_DOMAIN_POINT:
+ copy_spline_data_to_mesh(src.typed<T>(), offsets.vert, dst_attribute.data.typed<T>());
+ break;
+ case ATTR_DOMAIN_EDGE:
+ copy_spline_data_to_mesh(src.typed<T>(), offsets.edge, dst_attribute.data.typed<T>());
+ break;
+ case ATTR_DOMAIN_FACE:
+ copy_spline_data_to_mesh(src.typed<T>(), offsets.poly, dst_attribute.data.typed<T>());
+ break;
+ case ATTR_DOMAIN_CORNER:
+ copy_spline_data_to_mesh(src.typed<T>(), offsets.loop, dst_attribute.data.typed<T>());
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ });
+}
+
+static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve,
+ const CurveEval &profile,
+ const ResultOffsets &offsets,
+ ResultAttributes &attributes)
+{
+ if (!attributes.curve_spline_attributes.is_empty()) {
+ int i = 0;
+ curve.attributes.foreach_attribute(
+ [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) {
+ if (attributes.curve_spline_attributes[i]) {
+ copy_spline_attribute_to_mesh(*curve.attributes.get_for_read(id),
+ offsets,
+ *attributes.curve_spline_attributes[i]);
+ }
+ i++;
+ return true;
+ },
+ ATTR_DOMAIN_CURVE);
+ }
+ if (!attributes.profile_spline_attributes.is_empty()) {
+ int i = 0;
+ profile.attributes.foreach_attribute(
+ [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) {
+ if (attributes.profile_spline_attributes[i]) {
+ copy_spline_attribute_to_mesh(*profile.attributes.get_for_read(id),
+ offsets,
+ *attributes.profile_spline_attributes[i]);
+ }
+ i++;
+ return true;
+ },
+ ATTR_DOMAIN_CURVE);
+ }
+}
+
/**
* \note Normal calculation is by far the slowest part of calculations relating to the result mesh.
* Although it would be a sensible decision to use the better topology information available while
@@ -296,27 +668,50 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr
mesh->smoothresh = DEG2RADF(180.0f);
BKE_mesh_normals_tag_dirty(mesh);
+ ResultAttributes attributes = create_result_attributes(curve, profile, *mesh);
+
threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) {
for (const int i_spline : curves_range) {
+ const Spline &spline = *curves[i_spline];
+ if (spline.evaluated_points_size() == 0) {
+ continue;
+ }
const int spline_start_index = i_spline * profiles.size();
threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) {
for (const int i_profile : profiles_range) {
+ const Spline &profile = *profiles[i_profile];
const int i_mesh = spline_start_index + i_profile;
- spline_extrude_to_mesh_data(*curves[i_spline],
- *profiles[i_profile],
- offsets.vert[i_mesh],
- offsets.edge[i_mesh],
- offsets.loop[i_mesh],
- offsets.poly[i_mesh],
+ ResultInfo info{
+ spline,
+ profile,
+ offsets.vert[i_mesh],
+ offsets.edge[i_mesh],
+ offsets.loop[i_mesh],
+ offsets.poly[i_mesh],
+ spline.evaluated_points_size(),
+ spline.evaluated_edges_size(),
+ profile.evaluated_points_size(),
+ profile.evaluated_edges_size(),
+ };
+
+ spline_extrude_to_mesh_data(info,
{mesh->mvert, mesh->totvert},
{mesh->medge, mesh->totedge},
{mesh->mloop, mesh->totloop},
{mesh->mpoly, mesh->totpoly});
+
+ copy_point_domain_attributes_to_mesh(info, attributes);
}
});
}
});
+ copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes);
+
+ for (OutputAttribute &output_attribute : attributes.attributes) {
+ output_attribute.save();
+ }
+
return mesh;
}