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-04-15 18:14:54 +0300
committerHans Goudey <h.goudey@me.com>2022-04-15 18:15:48 +0300
commit7484f274dcdc9092dd5d85059e8d110572fe3b3c (patch)
treeb35e754bfbe5d62f5be6e890de2cd957a9adcad8
parentcc6db8921bdc497d75c495c843b913109adb9d4a (diff)
Curves: Port curve to mesh node to the new data-block
This commit changes the Curve to Mesh node to work with `Curves` instead of `CurveEval`. The change ends up basically completely rewriting the node, since the different attribute storage means that the decisions made previously don't make much sense anymore. The main loops are now "for each attribute: for each curve combination" rather than the other way around, with the goal of taking advantage of the locality of curve attributes. This improvement is quite noticeable with many small curves; I measured a 4-5x improvement (around 4-5s to <1s) when converting millions of curves to tens of millions of faces. I didn't obverse any change in performance compared to 3.1 with fewer curves though. The changes also solve an algorithmic flaw where any interpolated attributes would be evaluated for every curve combination instead of just once per curve. This can be a large improvement when there are many profile curves. The code relies heavily on a function `foreach_curve_combination` which calculates some basic information about each combination and calls a templated function. I made assumptions about unnecessary reads being removed by compiler optimizations. For further performance improvements in the future that might be an area to investigate. Another might be using a "for a group of curves: for each attribute: for each curve" pattern to increase the locality of memory access. Differential Revision: https://developer.blender.org/D14642
-rw-r--r--source/blender/blenkernel/BKE_curve_to_mesh.hh8
-rw-r--r--source/blender/blenkernel/BKE_curves.hh29
-rw-r--r--source/blender/blenkernel/intern/curve_to_mesh_convert.cc1092
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc42
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc4
-rw-r--r--source/blender/blenlib/BLI_vector.hh10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc13
7 files changed, 669 insertions, 529 deletions
diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh
index a49cb6eb7f5..6e657542e0f 100644
--- a/source/blender/blenkernel/BKE_curve_to_mesh.hh
+++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh
@@ -2,7 +2,7 @@
#pragma once
-struct CurveEval;
+struct CurvesGeometry;
struct Mesh;
/** \file
@@ -21,11 +21,13 @@ namespace blender::bke {
* changed anyway in a way that affects the normals. So currently this code uses the safer /
* simpler solution of deferring normal calculation to the rest of Blender.
*/
-Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, bool fill_caps);
+Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
+ const CurvesGeometry &profile,
+ bool fill_caps);
/**
* Create a loose-edge mesh based on the evaluated path of the curve's splines.
* Transfer curve attributes to the mesh.
*/
-Mesh *curve_to_wire_mesh(const CurveEval &curve);
+Mesh *curve_to_wire_mesh(const CurvesGeometry &curve);
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 3d912e10fb2..282e2a40bd0 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -328,6 +328,10 @@ class CurvesGeometry : public ::CurvesGeometry {
* calculated. That can be ensured with #ensure_evaluated_offsets.
*/
void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const;
+ /**
+ * Evaluate generic data for curve control points to the standard evaluated points of the curves.
+ */
+ void interpolate_to_evaluated(GSpan src, GMutableSpan dst) const;
private:
/**
@@ -446,6 +450,13 @@ bool segment_is_vector(Span<int8_t> handle_types_left,
bool last_cylic_segment_is_vector(Span<int8_t> handle_types_left, Span<int8_t> handle_types_right);
/**
+ * Return true if the handle types at the index are free (#BEZIER_HANDLE_FREE) or vector
+ * (#BEZIER_HANDLE_VECTOR). In these cases, directional continuitity from the previous and next
+ * evaluated segments is assumed not to be desired.
+ */
+bool point_is_sharp(Span<int8_t> handle_types_left, Span<int8_t> handle_types_right, int index);
+
+/**
* Calculate offsets into the curve's evaluated points for each control point. While most control
* point edges generate the number of edges specified by the resolution, vector segments only
* generate one edge.
@@ -715,4 +726,22 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Bezier Inline Methods
+ * \{ */
+
+namespace curves::bezier {
+
+inline bool point_is_sharp(const Span<int8_t> handle_types_left,
+ const Span<int8_t> handle_types_right,
+ const int index)
+{
+ return ELEM(handle_types_left[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) ||
+ ELEM(handle_types_right[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE);
+}
+
+} // namespace curves::bezier
+
+/** \} */
+
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
index 9b22a4c9726..c48d155f5ce 100644
--- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
+++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
@@ -9,65 +9,15 @@
#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_material.h"
#include "BKE_mesh.h"
-#include "BKE_spline.hh"
#include "BKE_curve_to_mesh.hh"
namespace blender::bke {
-/** 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,
- MutableSpan<MEdge> r_edges,
- const int vert_offset,
- const int edge_offset)
-{
- const int eval_size = spline.evaluated_points_size();
- for (const int i : IndexRange(eval_size - 1)) {
- MEdge &edge = r_edges[edge_offset + i];
- edge.v1 = vert_offset + i;
- edge.v2 = vert_offset + i + 1;
- edge.flag = ME_LOOSEEDGE;
- }
-
- if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
- MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1];
- edge.v1 = vert_offset + eval_size - 1;
- edge.v2 = vert_offset;
- edge.flag = ME_LOOSEEDGE;
- }
-
- Span<float3> positions = spline.evaluated_positions();
- Span<float3> tangents = spline.evaluated_tangents();
- Span<float3> normals = spline.evaluated_normals();
- VArray<float> radii = spline.interpolate_to_evaluated(spline.radii());
- for (const int i : IndexRange(eval_size)) {
- float4x4 point_matrix = float4x4::from_normalized_axis_data(
- positions[i], normals[i], tangents[i]);
- point_matrix.apply_scale(radii[i]);
-
- MVert &vert = r_verts[vert_offset + i];
- copy_v3_v3(vert.co, point_matrix * profile_vert);
- }
-}
-
static void mark_edges_sharp(MutableSpan<MEdge> edges)
{
for (MEdge &edge : edges) {
@@ -75,36 +25,50 @@ static void mark_edges_sharp(MutableSpan<MEdge> edges)
}
}
-static void spline_extrude_to_mesh_data(const ResultInfo &info,
- const bool fill_caps,
- MutableSpan<MVert> r_verts,
- MutableSpan<MEdge> r_edges,
- MutableSpan<MLoop> r_loops,
- MutableSpan<MPoly> r_polys)
+static void fill_mesh_topology(const int vert_offset,
+ const int edge_offset,
+ const int poly_offset,
+ const int loop_offset,
+ const int main_point_num,
+ const int profile_point_num,
+ const bool main_cyclic,
+ const bool profile_cyclic,
+ const bool fill_caps,
+ MutableSpan<MEdge> edges,
+ MutableSpan<MLoop> loops,
+ MutableSpan<MPoly> polys)
{
- const Spline &spline = info.spline;
- const Spline &profile = info.profile;
- if (info.profile_vert_len == 1) {
- vert_extrude_to_mesh_data(spline,
- profile.evaluated_positions()[0],
- r_verts,
- r_edges,
- info.vert_offset,
- info.edge_offset);
+ const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic);
+ const int profile_segment_num = curves::curve_segment_size(profile_point_num, profile_cyclic);
+
+ if (profile_point_num == 1) {
+ for (const int i : IndexRange(main_point_num - 1)) {
+ MEdge &edge = edges[edge_offset + i];
+ edge.v1 = vert_offset + i;
+ edge.v2 = vert_offset + i + 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ if (main_cyclic && main_segment_num > 1) {
+ MEdge &edge = edges[edge_offset + main_segment_num - 1];
+ edge.v1 = vert_offset + main_point_num - 1;
+ edge.v2 = vert_offset;
+ edge.flag = ME_LOOSEEDGE;
+ }
return;
}
/* Add the edges running along the length of the curve, starting at each profile vertex. */
- 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 main_edges_start = edge_offset;
+ for (const int i_profile : IndexRange(profile_point_num)) {
+ const int profile_edge_offset = main_edges_start + i_profile * main_segment_num;
+ for (const int i_ring : IndexRange(main_segment_num)) {
+ const int i_next_ring = (i_ring == main_point_num - 1) ? 0 : i_ring + 1;
- 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_vert_offset = vert_offset + profile_point_num * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_point_num * i_next_ring;
- MEdge &edge = r_edges[profile_edge_offset + i_ring];
+ MEdge &edge = edges[profile_edge_offset + i_ring];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = next_ring_vert_offset + i_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -112,16 +76,15 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info,
}
/* Add the edges running along each profile 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 profile_edges_start = main_edges_start + profile_point_num * main_segment_num;
+ for (const int i_ring : IndexRange(main_point_num)) {
+ const int ring_vert_offset = vert_offset + profile_point_num * i_ring;
- 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;
+ const int ring_edge_offset = profile_edges_start + i_ring * profile_segment_num;
+ for (const int i_profile : IndexRange(profile_segment_num)) {
+ const int i_next_profile = (i_profile == profile_point_num - 1) ? 0 : i_profile + 1;
- MEdge &edge = r_edges[ring_edge_offset + i_profile];
+ MEdge &edge = edges[ring_edge_offset + i_profile];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = ring_vert_offset + i_next_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -129,368 +92,410 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info,
}
/* Calculate poly and corner indices. */
- 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;
+ for (const int i_ring : IndexRange(main_segment_num)) {
+ const int i_next_ring = (i_ring == main_point_num - 1) ? 0 : i_ring + 1;
- 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_vert_offset = vert_offset + profile_point_num * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_point_num * 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_edge_start = profile_edges_start + profile_segment_num * i_ring;
+ const int next_ring_edge_offset = profile_edges_start + profile_segment_num * i_next_ring;
- 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;
+ const int ring_poly_offset = poly_offset + i_ring * profile_segment_num;
+ const int ring_loop_offset = loop_offset + i_ring * profile_segment_num * 4;
- for (const int i_profile : IndexRange(info.profile_edge_len)) {
+ for (const int i_profile : IndexRange(profile_segment_num)) {
const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4;
- const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1;
+ const int i_next_profile = (i_profile == profile_point_num - 1) ? 0 : i_profile + 1;
- 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;
+ const int main_edge_start = main_edges_start + main_segment_num * i_profile;
+ const int next_main_edge_start = main_edges_start + main_segment_num * i_next_profile;
- MPoly &poly = r_polys[ring_poly_offset + i_profile];
+ MPoly &poly = polys[ring_poly_offset + i_profile];
poly.loopstart = ring_segment_loop_offset;
poly.totloop = 4;
poly.flag = ME_SMOOTH;
- MLoop &loop_a = r_loops[ring_segment_loop_offset];
+ MLoop &loop_a = loops[ring_segment_loop_offset];
loop_a.v = ring_vert_offset + i_profile;
loop_a.e = ring_edge_start + i_profile;
- MLoop &loop_b = r_loops[ring_segment_loop_offset + 1];
+ MLoop &loop_b = loops[ring_segment_loop_offset + 1];
loop_b.v = ring_vert_offset + i_next_profile;
- loop_b.e = next_spline_edge_start + i_ring;
- MLoop &loop_c = r_loops[ring_segment_loop_offset + 2];
+ loop_b.e = next_main_edge_start + i_ring;
+ MLoop &loop_c = loops[ring_segment_loop_offset + 2];
loop_c.v = next_ring_vert_offset + i_next_profile;
loop_c.e = next_ring_edge_offset + i_profile;
- MLoop &loop_d = r_loops[ring_segment_loop_offset + 3];
+ MLoop &loop_d = loops[ring_segment_loop_offset + 3];
loop_d.v = next_ring_vert_offset + i_profile;
- loop_d.e = spline_edge_start + i_ring;
+ loop_d.e = main_edge_start + i_ring;
}
}
- const bool has_caps = fill_caps && profile.is_cyclic() && !spline.is_cyclic();
+ const bool has_caps = fill_caps && !main_cyclic && profile_cyclic;
if (has_caps) {
- const int poly_size = info.spline_edge_len * info.profile_edge_len;
- const int cap_loop_offset = info.loop_offset + poly_size * 4;
- const int cap_poly_offset = info.poly_offset + poly_size;
+ const int poly_size = main_segment_num * profile_segment_num;
+ const int cap_loop_offset = loop_offset + poly_size * 4;
+ const int cap_poly_offset = poly_offset + poly_size;
- MPoly &poly_start = r_polys[cap_poly_offset];
+ MPoly &poly_start = polys[cap_poly_offset];
poly_start.loopstart = cap_loop_offset;
- poly_start.totloop = info.profile_edge_len;
- MPoly &poly_end = r_polys[cap_poly_offset + 1];
- poly_end.loopstart = cap_loop_offset + info.profile_edge_len;
- poly_end.totloop = info.profile_edge_len;
-
- const int last_ring_index = info.spline_vert_len - 1;
- const int last_ring_vert_offset = info.vert_offset + info.profile_vert_len * last_ring_index;
- const int last_ring_edge_offset = profile_edges_start +
- info.profile_edge_len * last_ring_index;
-
- for (const int i : IndexRange(info.profile_edge_len)) {
- const int i_inv = info.profile_edge_len - i - 1;
- MLoop &loop_start = r_loops[cap_loop_offset + i];
- loop_start.v = info.vert_offset + i_inv;
- loop_start.e = profile_edges_start + ((i == (info.profile_edge_len - 1)) ?
- (info.profile_edge_len - 1) :
- (i_inv - 1));
- MLoop &loop_end = r_loops[cap_loop_offset + info.profile_edge_len + i];
+ poly_start.totloop = profile_segment_num;
+ MPoly &poly_end = polys[cap_poly_offset + 1];
+ poly_end.loopstart = cap_loop_offset + profile_segment_num;
+ poly_end.totloop = profile_segment_num;
+
+ const int last_ring_index = main_point_num - 1;
+ const int last_ring_vert_offset = vert_offset + profile_point_num * last_ring_index;
+ const int last_ring_edge_offset = profile_edges_start + profile_segment_num * last_ring_index;
+
+ for (const int i : IndexRange(profile_segment_num)) {
+ const int i_inv = profile_segment_num - i - 1;
+ MLoop &loop_start = loops[cap_loop_offset + i];
+ loop_start.v = vert_offset + i_inv;
+ loop_start.e = profile_edges_start +
+ ((i == (profile_segment_num - 1)) ? (profile_segment_num - 1) : (i_inv - 1));
+ MLoop &loop_end = loops[cap_loop_offset + profile_segment_num + i];
loop_end.v = last_ring_vert_offset + i;
loop_end.e = last_ring_edge_offset + i;
}
- mark_edges_sharp(r_edges.slice(profile_edges_start, info.profile_edge_len));
- mark_edges_sharp(r_edges.slice(last_ring_edge_offset, info.profile_edge_len));
+ mark_edges_sharp(edges.slice(profile_edges_start, profile_segment_num));
+ mark_edges_sharp(edges.slice(last_ring_edge_offset, profile_segment_num));
}
+}
- /* Calculate the positions of each profile ring profile along the spline. */
- Span<float3> positions = spline.evaluated_positions();
- Span<float3> tangents = spline.evaluated_tangents();
- Span<float3> normals = spline.evaluated_normals();
- Span<float3> profile_positions = profile.evaluated_positions();
-
- VArray<float> radii = spline.interpolate_to_evaluated(spline.radii());
- 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 = 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]);
- }
+static void mark_bezier_vector_edges_sharp(const int profile_point_num,
+ const int main_segment_num,
+ const Span<int> control_point_offsets,
+ const Span<int8_t> handle_types_left,
+ const Span<int8_t> handle_types_right,
+ MutableSpan<MEdge> edges)
+{
+ const int main_edges_start = 0;
+ if (curves::bezier::point_is_sharp(handle_types_left, handle_types_right, 0)) {
+ mark_edges_sharp(edges.slice(main_edges_start, main_segment_num));
}
- /* Mark edge loops from sharp vector control points sharp. */
- if (profile.type() == CURVE_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 + info.spline_edge_len * control_point_offsets[i],
- info.spline_edge_len));
- }
+ for (const int i : IndexRange(profile_point_num).drop_front(1)) {
+ if (curves::bezier::point_is_sharp(handle_types_left, handle_types_right, i)) {
+ mark_edges_sharp(edges.slice(
+ main_edges_start + main_segment_num * control_point_offsets[i - 1], main_segment_num));
}
}
}
-static inline int spline_extrude_vert_size(const Spline &curve, const Spline &profile)
+static void fill_mesh_positions(const int main_point_num,
+ const int profile_point_num,
+ const Span<float3> main_positions,
+ const Span<float3> profile_positions,
+ const Span<float3> tangents,
+ const Span<float3> normals,
+ const Span<float> radii,
+ MutableSpan<MVert> mesh_positions)
{
- return curve.evaluated_points_size() * profile.evaluated_points_size();
-}
+ if (profile_point_num == 1) {
+ for (const int i_ring : IndexRange(main_point_num)) {
+ float4x4 point_matrix = float4x4::from_normalized_axis_data(
+ main_positions[i_ring], normals[i_ring], tangents[i_ring]);
+ if (!radii.is_empty()) {
+ point_matrix.apply_scale(radii[i_ring]);
+ }
-static inline int spline_extrude_edge_size(const Spline &curve, const Spline &profile)
-{
- /* Add the ring edges, with one ring for every curve vertex, and the edge loops
- * that run along the length of the curve, starting on the first profile. */
- return curve.evaluated_points_size() * profile.evaluated_edges_size() +
- curve.evaluated_edges_size() * profile.evaluated_points_size();
-}
+ MVert &vert = mesh_positions[i_ring];
+ copy_v3_v3(vert.co, point_matrix * profile_positions.first());
+ }
+ }
+ else {
+ for (const int i_ring : IndexRange(main_point_num)) {
+ float4x4 point_matrix = float4x4::from_normalized_axis_data(
+ main_positions[i_ring], normals[i_ring], tangents[i_ring]);
+ if (!radii.is_empty()) {
+ point_matrix.apply_scale(radii[i_ring]);
+ }
-static inline int spline_extrude_loop_size(const Spline &curve,
- const Spline &profile,
- const bool fill_caps)
-{
- const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4;
- const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic();
- const int caps = has_caps ? profile.evaluated_edges_size() * 2 : 0;
- return tube + caps;
+ const int ring_vert_start = i_ring * profile_point_num;
+ for (const int i_profile : IndexRange(profile_point_num)) {
+ MVert &vert = mesh_positions[ring_vert_start + i_profile];
+ copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
+ }
+ }
+ }
}
-static inline int spline_extrude_poly_size(const Spline &curve,
- const Spline &profile,
- const bool fill_caps)
+struct CurvesInfo {
+ const CurvesGeometry &main;
+ const CurvesGeometry &profile;
+
+ /* Make sure these are spans because they are potentially accessed many times. */
+ VArray_Span<bool> main_cyclic;
+ VArray_Span<bool> profile_cyclic;
+
+ /* TODO: Remove once these are cached on #CurvesGeometry. */
+ std::array<int, CURVE_TYPES_NUM> main_type_counts;
+ std::array<int, CURVE_TYPES_NUM> profile_type_counts;
+};
+static CurvesInfo get_curves_info(const CurvesGeometry &main, const CurvesGeometry &profile)
{
- const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size();
- const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic();
- const int caps = has_caps ? 2 : 0;
- return tube + caps;
+ return {main,
+ profile,
+ main.cyclic(),
+ profile.cyclic(),
+ main.count_curve_types(),
+ profile.count_curve_types()};
}
struct ResultOffsets {
+ /** The total number of curve combinations. */
+ int total;
+
+ /** Offsets into the result mesh for each combination. */
Array<int> vert;
Array<int> edge;
Array<int> loop;
Array<int> poly;
+
+ /* The indices of the main and profile curves that form each combination. */
+ Array<int> main_indices;
+ Array<int> profile_indices;
};
-static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles,
- Span<SplinePtr> curves,
- const bool fill_caps)
+static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool fill_caps)
{
- const int total = profiles.size() * curves.size();
- Array<int> vert(total + 1);
- Array<int> edge(total + 1);
- Array<int> loop(total + 1);
- Array<int> poly(total + 1);
+ ResultOffsets result;
+ result.total = info.main.curves_num() * info.profile.curves_num();
+ result.vert.reinitialize(result.total + 1);
+ result.edge.reinitialize(result.total + 1);
+ result.loop.reinitialize(result.total + 1);
+ result.poly.reinitialize(result.total + 1);
+
+ result.main_indices.reinitialize(result.total);
+ result.profile_indices.reinitialize(result.total);
+
+ info.main.ensure_evaluated_offsets();
+ info.profile.ensure_evaluated_offsets();
int mesh_index = 0;
int vert_offset = 0;
int edge_offset = 0;
int loop_offset = 0;
int poly_offset = 0;
- for (const int i_spline : curves.index_range()) {
- for (const int i_profile : profiles.index_range()) {
- vert[mesh_index] = vert_offset;
- edge[mesh_index] = edge_offset;
- loop[mesh_index] = loop_offset;
- poly[mesh_index] = poly_offset;
- vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]);
- edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]);
- loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile], fill_caps);
- poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile], fill_caps);
+ for (const int i_main : info.main.curves_range()) {
+ const bool main_cyclic = info.main_cyclic[i_main];
+ const int main_point_num = info.main.evaluated_points_for_curve(i_main).size();
+ const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic);
+ for (const int i_profile : info.profile.curves_range()) {
+ result.vert[mesh_index] = vert_offset;
+ result.edge[mesh_index] = edge_offset;
+ result.loop[mesh_index] = loop_offset;
+ result.poly[mesh_index] = poly_offset;
+
+ result.main_indices[mesh_index] = i_main;
+ result.profile_indices[mesh_index] = i_profile;
+
+ const bool profile_cyclic = info.profile_cyclic[i_profile];
+ const int profile_point_num = info.profile.evaluated_points_for_curve(i_profile).size();
+ const int profile_segment_num = curves::curve_segment_size(profile_point_num,
+ profile_cyclic);
+
+ const bool has_caps = fill_caps && !main_cyclic && profile_cyclic;
+ const int tube_face_num = main_segment_num * profile_segment_num;
+
+ vert_offset += main_point_num * profile_point_num;
+
+ /* Add the ring edges, with one ring for every curve vertex, and the edge loops
+ * that run along the length of the curve, starting on the first profile. */
+ edge_offset += main_point_num * profile_segment_num + main_segment_num * profile_point_num;
+
+ /* Add two cap N-gons for every ending. */
+ poly_offset += tube_face_num + (has_caps ? 2 : 0);
+
+ /* All faces on the tube are quads, and all cap faces are N-gons with an edge for each
+ * profile edge. */
+ loop_offset += tube_face_num * 4 + (has_caps ? profile_segment_num * 2 : 0);
+
mesh_index++;
}
}
- vert.last() = vert_offset;
- edge.last() = edge_offset;
- loop.last() = loop_offset;
- poly.last() = poly_offset;
- return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)};
+ result.vert.last() = vert_offset;
+ result.edge.last() = edge_offset;
+ result.loop.last() = loop_offset;
+ result.poly.last() = poly_offset;
+
+ return result;
}
-static AttributeDomain get_result_attribute_domain(const MeshComponent &component,
- const AttributeIDRef &attribute_id)
+static AttributeDomain get_attribute_domain_for_mesh(const MeshComponent &mesh,
+ 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)) {
+ if (!mesh.attribute_is_builtin(attribute_id)) {
return ATTR_DOMAIN_POINT;
}
- std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(attribute_id);
+ std::optional<AttributeMetaData> meta_data = mesh.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 ATTR_DOMAIN_POINT;
}
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)
+static bool should_add_attribute_to_mesh(const CurveComponent &curve_component,
+ const MeshComponent &mesh_component,
+ const AttributeIDRef &id)
{
- 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;
+ /* The position attribute has special non-generic evaluation. */
+ if (id.is_named() && id.name() == "position") {
+ return false;
+ }
+ /* Don't propagate built-in curves attributes that are not built-in on meshes. */
+ if (curve_component.attribute_is_builtin(id) && !mesh_component.attribute_is_builtin(id)) {
+ return false;
}
+ if (!id.should_be_kept()) {
+ return false;
+ }
+ return true;
+}
- GMutableSpan span = attribute.as_span();
- r_attributes.append(std::move(attribute));
- return std::make_optional<ResultAttributeData>({span, domain});
+static GSpan evaluated_attribute_if_necessary(const GVArray &src,
+ const CurvesGeometry &curves,
+ const Span<int> type_counts,
+ Vector<std::byte> &buffer)
+{
+ if (type_counts[CURVE_TYPE_POLY] == curves.curves_num() && src.is_span()) {
+ return src.get_internal_span();
+ }
+ buffer.reinitialize(curves.evaluated_points_num() * src.type().size());
+ GMutableSpan eval{src.type(), buffer.data(), curves.evaluated_points_num()};
+ curves.interpolate_to_evaluated(src.get_internal_span(), eval);
+ return eval;
}
-/**
- * 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 names 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;
+/** Information at a specific combination of main and profile curves. */
+struct CombinationInfo {
+ int i_main;
+ int i_profile;
+
+ IndexRange main_points;
+ IndexRange profile_points;
+
+ bool main_cyclic;
+ bool profile_cyclic;
+
+ int main_segment_num;
+ int profile_segment_num;
+
+ IndexRange vert_range;
+ IndexRange edge_range;
+ IndexRange poly_range;
+ IndexRange loop_range;
};
-static ResultAttributes create_result_attributes(const CurveEval &curve,
- const CurveEval &profile,
- MeshComponent &mesh_component)
+template<typename Fn>
+static void foreach_curve_combination(const CurvesInfo &info,
+ const ResultOffsets &offsets,
+ const Fn &fn)
{
- 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;
+ threading::parallel_for(IndexRange(offsets.total), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const int i_main = offsets.main_indices[i];
+ const int i_profile = offsets.profile_indices[i];
+
+ const IndexRange main_points = info.main.evaluated_points_for_curve(i_main);
+ const IndexRange profile_points = info.profile.evaluated_points_for_curve(i_profile);
+
+ const bool main_cyclic = info.main_cyclic[i_main];
+ const bool profile_cyclic = info.profile_cyclic[i_profile];
+
+ /* Pass all information in a struct to avoid repeating arguments in many lambdas.
+ * The idea is that inlining `fn` will help avoid accessing unnecessary information,
+ * though that may or may not happen in practice. */
+ fn(CombinationInfo{i_main,
+ i_profile,
+ main_points,
+ profile_points,
+ main_cyclic,
+ profile_cyclic,
+ curves::curve_segment_size(main_points.size(), main_cyclic),
+ curves::curve_segment_size(profile_points.size(), profile_cyclic),
+ offsets_to_range(offsets.vert.as_span(), i),
+ offsets_to_range(offsets.edge.as_span(), i),
+ offsets_to_range(offsets.poly.as_span(), i),
+ offsets_to_range(offsets.loop.as_span(), i)});
+ }
+ });
}
template<typename T>
-static void copy_curve_point_data_to_mesh_verts(const Span<T> src,
- const ResultInfo &info,
- MutableSpan<T> dst)
+static void copy_main_point_data_to_mesh_verts(const Span<T> src,
+ const int profile_point_num,
+ 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]);
+ for (const int i_ring : src.index_range()) {
+ const int ring_vert_start = i_ring * profile_point_num;
+ dst.slice(ring_vert_start, profile_point_num).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)
+static void copy_main_point_data_to_mesh_edges(const Span<T> src,
+ const int profile_point_num,
+ const int main_segment_num,
+ const int profile_segment_num,
+ 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]);
+ const int edges_start = profile_point_num * main_segment_num;
+ for (const int i_ring : src.index_range()) {
+ const int ring_edge_start = edges_start + profile_segment_num * i_ring;
+ dst.slice(ring_edge_start, profile_segment_num).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)
+static void copy_main_point_data_to_mesh_faces(const Span<T> src,
+ const int main_segment_num,
+ const int profile_segment_num,
+ 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]);
+ for (const int i_ring : IndexRange(main_segment_num)) {
+ const int ring_face_start = profile_segment_num * i_ring;
+ dst.slice(ring_face_start, profile_segment_num).fill(src[i_ring]);
}
}
-static void copy_curve_point_attribute_to_mesh(const GSpan src,
- const ResultInfo &info,
- ResultAttributeData &dst)
+static void copy_main_point_domain_attribute_to_mesh(const CurvesInfo &curves_info,
+ const ResultOffsets &offsets,
+ const AttributeDomain dst_domain,
+ const GSpan src_all,
+ GMutableSpan dst_all)
{
- GVArray 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) {
+ attribute_math::convert_to_static_type(src_all.type(), [&](auto dummy) {
using T = decltype(dummy);
- switch (dst.domain) {
+ const Span<T> src = src_all.typed<T>();
+ MutableSpan<T> dst = dst_all.typed<T>();
+ switch (dst_domain) {
case ATTR_DOMAIN_POINT:
- copy_curve_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_main_point_data_to_mesh_verts(
+ src.slice(info.main_points), info.profile_points.size(), dst.slice(info.vert_range));
+ });
break;
case ATTR_DOMAIN_EDGE:
- copy_curve_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_main_point_data_to_mesh_edges(src.slice(info.main_points),
+ info.profile_points.size(),
+ info.main_segment_num,
+ info.profile_segment_num,
+ dst.slice(info.edge_range));
+ });
break;
case ATTR_DOMAIN_FACE:
- copy_curve_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_main_point_data_to_mesh_faces(src.slice(info.main_points),
+ info.main_segment_num,
+ info.profile_segment_num,
+ dst.slice(info.poly_range));
+ });
break;
case ATTR_DOMAIN_CORNER:
/* Unsupported for now, since there are no builtin attributes to convert into. */
@@ -504,12 +509,12 @@ static void copy_curve_point_attribute_to_mesh(const GSpan src,
template<typename T>
static void copy_profile_point_data_to_mesh_verts(const Span<T> src,
- const ResultInfo &info,
+ const int main_point_num,
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)) {
+ for (const int i_ring : IndexRange(main_point_num)) {
+ const int profile_vert_start = i_ring * src.size();
+ for (const int i_profile : src.index_range()) {
dst[profile_vert_start + i_profile] = src[i_profile];
}
}
@@ -517,46 +522,59 @@ static void copy_profile_point_data_to_mesh_verts(const Span<T> src,
template<typename T>
static void copy_profile_point_data_to_mesh_edges(const Span<T> src,
- const ResultInfo &info,
+ const int main_segment_num,
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]);
+ for (const int i_profile : src.index_range()) {
+ const int profile_edge_offset = i_profile * main_segment_num;
+ dst.slice(profile_edge_offset, main_segment_num).fill(src[i_profile]);
}
}
template<typename T>
static void copy_profile_point_data_to_mesh_faces(const Span<T> src,
- const ResultInfo &info,
+ const int main_segment_num,
+ const int profile_segment_num,
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)) {
+ for (const int i_ring : IndexRange(main_segment_num)) {
+ const int profile_face_start = i_ring * profile_segment_num;
+ for (const int i_profile : IndexRange(profile_segment_num)) {
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)
+static void copy_profile_point_domain_attribute_to_mesh(const CurvesInfo &curves_info,
+ const ResultOffsets &offsets,
+ const AttributeDomain dst_domain,
+ const GSpan src_all,
+ GMutableSpan dst_all)
{
- GVArray 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) {
+ attribute_math::convert_to_static_type(src_all.type(), [&](auto dummy) {
using T = decltype(dummy);
- switch (dst.domain) {
+ const Span<T> src = src_all.typed<T>();
+ MutableSpan<T> dst = dst_all.typed<T>();
+ switch (dst_domain) {
case ATTR_DOMAIN_POINT:
- copy_profile_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_profile_point_data_to_mesh_verts(
+ src.slice(info.profile_points), info.main_points.size(), dst.slice(info.vert_range));
+ });
break;
case ATTR_DOMAIN_EDGE:
- copy_profile_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_profile_point_data_to_mesh_edges(
+ src.slice(info.profile_points), info.main_segment_num, dst.slice(info.edge_range));
+ });
break;
case ATTR_DOMAIN_FACE:
- copy_profile_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_profile_point_data_to_mesh_faces(src.slice(info.profile_points),
+ info.main_segment_num,
+ info.profile_segment_num,
+ dst.slice(info.poly_range));
+ });
break;
case ATTR_DOMAIN_CORNER:
/* Unsupported for now, since there are no builtin attributes to convert into. */
@@ -568,198 +586,236 @@ static void copy_profile_point_attribute_to_mesh(const GSpan src,
});
}
-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)
+static void copy_indices_to_offset_ranges(const VArray<T> &src,
+ const Span<int> curve_indices,
+ const Span<int> mesh_offsets,
+ MutableSpan<T> dst)
{
- for (const int i : IndexRange(src.size())) {
- dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]);
- }
+ /* This unnecessarily instantiates the "is single" case (which should be handled elsewhere if
+ * it's ever used for attributes), but the alternative is duplicating the function for spans and
+ * other virtual arrays. */
+ devirtualize_varray(src, [&](const auto &src) {
+ threading::parallel_for(curve_indices.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ dst.slice(offsets_to_range(mesh_offsets, i)).fill(src[curve_indices[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)
+static void copy_curve_domain_attribute_to_mesh(const ResultOffsets &mesh_offsets,
+ const Span<int> curve_indices,
+ const AttributeDomain dst_domain,
+ const GVArray &src,
+ GMutableSpan dst)
{
+ Span<int> offsets;
+ switch (dst_domain) {
+ case ATTR_DOMAIN_POINT:
+ offsets = mesh_offsets.vert;
+ break;
+ case ATTR_DOMAIN_EDGE:
+ offsets = mesh_offsets.edge;
+ break;
+ case ATTR_DOMAIN_FACE:
+ offsets = mesh_offsets.poly;
+ break;
+ case ATTR_DOMAIN_CORNER:
+ offsets = mesh_offsets.loop;
+ break;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
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;
- }
+ copy_indices_to_offset_ranges(src.typed<T>(), curve_indices, offsets, dst.typed<T>());
});
}
-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);
- }
-}
-
-Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, const bool fill_caps)
+Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
+ const CurvesGeometry &profile,
+ const bool fill_caps)
{
- Span<SplinePtr> profiles = profile.splines();
- Span<SplinePtr> curves = curve.splines();
+ const CurvesInfo curves_info = get_curves_info(main, profile);
- const ResultOffsets offsets = calculate_result_offsets(profiles, curves, fill_caps);
+ const ResultOffsets offsets = calculate_result_offsets(curves_info, fill_caps);
if (offsets.vert.last() == 0) {
return nullptr;
}
Mesh *mesh = BKE_mesh_new_nomain(
offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last());
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
mesh->flag |= ME_AUTOSMOOTH;
mesh->smoothresh = DEG2RADF(180.0f);
BKE_mesh_normals_tag_dirty(mesh);
+ MutableSpan<MVert> verts(mesh->mvert, mesh->totvert);
+ MutableSpan<MEdge> edges(mesh->medge, mesh->totedge);
+ MutableSpan<MLoop> loops(mesh->mloop, mesh->totloop);
+ MutableSpan<MPoly> polys(mesh->mpoly, mesh->totpoly);
+
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ fill_mesh_topology(info.vert_range.start(),
+ info.edge_range.start(),
+ info.poly_range.start(),
+ info.loop_range.start(),
+ info.main_points.size(),
+ info.profile_points.size(),
+ info.main_cyclic,
+ info.profile_cyclic,
+ fill_caps,
+ edges,
+ loops,
+ polys);
+ });
+
+ const Span<float3> main_positions = main.evaluated_positions();
+ const Span<float3> tangents = main.evaluated_tangents();
+ const Span<float3> normals = main.evaluated_normals();
+ const Span<float3> profile_positions = profile.evaluated_positions();
+
+ Vector<std::byte> eval_buffer;
+
+ Curves main_id = {nullptr};
+ main_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(main);
+ CurveComponent main_component;
+ main_component.replace(&main_id, GeometryOwnershipType::Editable);
+
+ Curves profile_id = {nullptr};
+ profile_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(profile);
+ CurveComponent profile_component;
+ profile_component.replace(&profile_id, GeometryOwnershipType::Editable);
+
+ Span<float> radii = {};
+ if (main_component.attribute_exists("radius")) {
+ radii = evaluated_attribute_if_necessary(
+ main_component.attribute_get_for_read<float>("radius", ATTR_DOMAIN_POINT, 1.0f),
+ main,
+ curves_info.main_type_counts,
+ eval_buffer)
+ .typed<float>();
+ }
+
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ fill_mesh_positions(info.main_points.size(),
+ info.profile_points.size(),
+ main_positions.slice(info.main_points),
+ profile_positions.slice(info.profile_points),
+ tangents.slice(info.main_points),
+ normals.slice(info.main_points),
+ radii.is_empty() ? radii : radii.slice(info.main_points),
+ verts.slice(info.vert_range));
+ });
+
+ if (curves_info.profile_type_counts[CURVE_TYPE_BEZIER] > 0) {
+ const VArray<int8_t> curve_types = profile.curve_types();
+ const VArray_Span<int8_t> handle_types_left{profile.handle_types_left()};
+ const VArray_Span<int8_t> handle_types_right{profile.handle_types_right()};
+
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ if (curve_types[info.i_profile] == CURVE_TYPE_BEZIER) {
+ const IndexRange points = profile.points_for_curve(info.i_profile);
+ mark_bezier_vector_edges_sharp(points.size(),
+ info.main_segment_num,
+ profile.bezier_evaluated_offsets_for_curve(info.i_profile),
+ handle_types_left.slice(points),
+ handle_types_right.slice(points),
+ edges.slice(info.edge_range));
+ }
+ });
+ }
+
+ Set<AttributeIDRef> main_attributes;
- /* Create the mesh component for retrieving attributes at this scope, since output attributes
- * can keep a reference to the component for updating after retrieving write access. */
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- ResultAttributes attributes = create_result_attributes(curve, profile, mesh_component);
- 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;
- 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,
- fill_caps,
- {mesh->mvert, mesh->totvert},
- {mesh->medge, mesh->totedge},
- {mesh->mloop, mesh->totloop},
- {mesh->mpoly, mesh->totpoly});
-
- copy_point_domain_attributes_to_mesh(info, attributes);
- }
- });
+ main_component.attribute_foreach([&](const AttributeIDRef &id,
+ const AttributeMetaData meta_data) {
+ if (!should_add_attribute_to_mesh(main_component, mesh_component, id)) {
+ return true;
}
+ main_attributes.add_new(id);
+
+ const AttributeDomain src_domain = meta_data.domain;
+ const CustomDataType type = meta_data.data_type;
+ GVArray src = main_component.attribute_try_get_for_read(id, src_domain, type);
+
+ const AttributeDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id);
+ OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type);
+ if (!dst) {
+ return true;
+ }
+
+ if (src_domain == ATTR_DOMAIN_POINT) {
+ copy_main_point_domain_attribute_to_mesh(
+ curves_info,
+ offsets,
+ dst_domain,
+ evaluated_attribute_if_necessary(src, main, curves_info.main_type_counts, eval_buffer),
+ dst.as_span());
+ }
+ else if (src_domain == ATTR_DOMAIN_CURVE) {
+ copy_curve_domain_attribute_to_mesh(
+ offsets, offsets.main_indices, dst_domain, src, dst.as_span());
+ }
+
+ dst.save();
+ return true;
});
- copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes);
+ profile_component.attribute_foreach([&](const AttributeIDRef &id,
+ const AttributeMetaData meta_data) {
+ if (main_attributes.contains(id)) {
+ return true;
+ }
+ if (!should_add_attribute_to_mesh(profile_component, mesh_component, id)) {
+ return true;
+ }
+ const AttributeDomain src_domain = meta_data.domain;
+ const CustomDataType type = meta_data.data_type;
+ GVArray src = profile_component.attribute_try_get_for_read(id, src_domain, type);
+
+ const AttributeDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id);
+ OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type);
+ if (!dst) {
+ return true;
+ }
+
+ if (src_domain == ATTR_DOMAIN_POINT) {
+ copy_profile_point_domain_attribute_to_mesh(
+ curves_info,
+ offsets,
+ dst_domain,
+ evaluated_attribute_if_necessary(
+ src, profile, curves_info.profile_type_counts, eval_buffer),
+ dst.as_span());
+ }
+ else if (src_domain == ATTR_DOMAIN_CURVE) {
+ copy_curve_domain_attribute_to_mesh(
+ offsets, offsets.profile_indices, dst_domain, src, dst.as_span());
+ }
- for (OutputAttribute &output_attribute : attributes.attributes) {
- output_attribute.save();
- }
+ dst.save();
+ return true;
+ });
return mesh;
}
-static CurveEval get_curve_single_vert()
+static CurvesGeometry get_curve_single_vert()
{
- CurveEval curve;
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- spline->resize(1.0f);
- spline->positions().fill(float3(0));
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
- curve.add_spline(std::move(spline));
-
- return curve;
+ CurvesGeometry curves(1, 1);
+ curves.offsets_for_write().last() = 1;
+ curves.positions_for_write().fill(float3(0));
+
+ return curves;
}
-Mesh *curve_to_wire_mesh(const CurveEval &curve)
+Mesh *curve_to_wire_mesh(const CurvesGeometry &curve)
{
- static const CurveEval vert_curve = get_curve_single_vert();
+ static const CurvesGeometry vert_curve = get_curve_single_vert();
return curve_to_mesh_sweep(curve, vert_curve, false);
}
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 24e156c3c4d..8e97884516c 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -840,6 +840,48 @@ void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
BLI_assert_unreachable();
}
+void CurvesGeometry::interpolate_to_evaluated(const GSpan src, GMutableSpan dst) const
+{
+ BLI_assert(!this->runtime->offsets_cache_dirty);
+ BLI_assert(!this->runtime->nurbs_basis_cache_dirty);
+ const VArray<int8_t> types = this->curve_types();
+ const VArray<int> resolution = this->resolution();
+ const VArray<bool> cyclic = this->cyclic();
+ const VArray<int8_t> nurbs_orders = this->nurbs_orders();
+ const Span<float> nurbs_weights = this->nurbs_weights();
+
+ threading::parallel_for(this->curves_range(), 512, [&](IndexRange curves_range) {
+ for (const int curve_index : curves_range) {
+ const IndexRange points = this->points_for_curve(curve_index);
+ const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
+ switch (types[curve_index]) {
+ case CURVE_TYPE_CATMULL_ROM:
+ curves::catmull_rom::interpolate_to_evaluated(src.slice(points),
+ cyclic[curve_index],
+ resolution[curve_index],
+ dst.slice(evaluated_points));
+ continue;
+ case CURVE_TYPE_POLY:
+ dst.slice(evaluated_points).copy_from(src.slice(points));
+ continue;
+ case CURVE_TYPE_BEZIER:
+ curves::bezier::interpolate_to_evaluated(
+ src.slice(points),
+ this->runtime->bezier_evaluated_offsets.as_span().slice(points),
+ dst.slice(evaluated_points));
+ continue;
+ case CURVE_TYPE_NURBS:
+ curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
+ nurbs_orders[curve_index],
+ nurbs_weights.slice(points),
+ src.slice(points),
+ dst.slice(evaluated_points));
+ continue;
+ }
+ }
+ });
+}
+
void CurvesGeometry::ensure_evaluated_lengths() const
{
if (!this->runtime->length_cache_dirty) {
diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc
index defca433968..ff953ef5b46 100644
--- a/source/blender/blenkernel/intern/mesh_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_convert.cc
@@ -25,6 +25,7 @@
#include "BLI_utildefines.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_curves.hh"
#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
@@ -970,8 +971,7 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o
}
const Curves *curves = get_evaluated_curves_from_object(evaluated_object);
if (curves) {
- std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
- return blender::bke::curve_to_wire_mesh(*curve);
+ return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry));
}
return nullptr;
}
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index da9ab9c313e..acf47f67168 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -387,6 +387,16 @@ class Vector {
}
/**
+ * Reset the size of the vector so that it contains new_size elements.
+ * All existing elements are destructed, and not copied if the data must be reallocated.
+ */
+ void reinitialize(const int64_t new_size)
+ {
+ this->clear();
+ this->resize(new_size);
+ }
+
+ /**
* Afterwards the vector has 0 elements, but will still have
* memory to be refilled again.
*/
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 e7a8c61290b..903a5e7c1d7 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
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "BKE_curve_to_mesh.hh"
@@ -27,17 +27,18 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const GeometrySet &profile_set,
const bool fill_caps)
{
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *geometry_set.get_curves_for_read());
+ const Curves &curves = *geometry_set.get_curves_for_read();
+
const Curves *profile_curves = profile_set.get_curves_for_read();
if (profile_curves == nullptr) {
- Mesh *mesh = bke::curve_to_wire_mesh(*curve);
+ Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry));
geometry_set.replace_mesh(mesh);
}
else {
- const std::unique_ptr<CurveEval> profile_curve = curves_to_curve_eval(*profile_curves);
- Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps);
+ Mesh *mesh = bke::curve_to_mesh_sweep(bke::CurvesGeometry::wrap(curves.geometry),
+ bke::CurvesGeometry::wrap(profile_curves->geometry),
+ fill_caps);
geometry_set.replace_mesh(mesh);
}
}