From bc2f4dd8b408eee35353c18a44ce0dc5b51394a9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 25 Oct 2021 09:10:44 -0500 Subject: Geometry Nodes: Add "Fill Caps" option to curve to mesh node This adds an option to fill the ends of the generated mesh for each spline combination with an N-gon. The resulting mesh is manifold, so it can be used for operations like Boolean. Differential Revision: https://developer.blender.org/D12982 --- source/blender/blenkernel/BKE_curve_to_mesh.hh | 2 +- .../blenkernel/intern/curve_to_mesh_convert.cc | 62 ++++++++++++++++++---- .../nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 15 ++++-- 3 files changed, 64 insertions(+), 15 deletions(-) (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh index 87bec6203a9..fb077425336 100644 --- a/source/blender/blenkernel/BKE_curve_to_mesh.hh +++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh @@ -25,7 +25,7 @@ struct CurveEval; namespace blender::bke { -Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile); +Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, bool fill_caps); Mesh *curve_to_wire_mesh(const CurveEval &curve); } // 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 5f2f945192c..b3957e57920 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -88,6 +88,7 @@ static void mark_edges_sharp(MutableSpan edges) } static void spline_extrude_to_mesh_data(const ResultInfo &info, + const bool fill_caps, MutableSpan r_verts, MutableSpan r_edges, MutableSpan r_loops, @@ -180,6 +181,36 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, } } + if (fill_caps && profile.is_cyclic()) { + 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; + + MPoly &poly_start = r_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)) { + MLoop &loop_start = r_loops[cap_loop_offset + i]; + loop_start.v = info.vert_offset + i; + loop_start.e = profile_edges_start + i; + MLoop &loop_end = r_loops[cap_loop_offset + info.profile_edge_len + 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)); + } + /* Calculate the positions of each profile ring profile along the spline. */ Span positions = spline.evaluated_positions(); Span tangents = spline.evaluated_tangents(); @@ -226,14 +257,22 @@ static inline int spline_extrude_edge_size(const Spline &curve, const Spline &pr curve.evaluated_edges_size() * profile.evaluated_points_size(); } -static inline int spline_extrude_loop_size(const Spline &curve, const Spline &profile) +static inline int spline_extrude_loop_size(const Spline &curve, + const Spline &profile, + const bool fill_caps) { - return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; + const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; + const int caps = (fill_caps && profile.is_cyclic()) ? profile.evaluated_edges_size() * 2 : 0; + return tube + caps; } -static inline int spline_extrude_poly_size(const Spline &curve, const Spline &profile) +static inline int spline_extrude_poly_size(const Spline &curve, + const Spline &profile, + const bool fill_caps) { - return curve.evaluated_edges_size() * profile.evaluated_edges_size(); + const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size(); + const int caps = (fill_caps && profile.is_cyclic()) ? 2 : 0; + return tube + caps; } struct ResultOffsets { @@ -242,7 +281,9 @@ struct ResultOffsets { Array loop; Array poly; }; -static ResultOffsets calculate_result_offsets(Span profiles, Span curves) +static ResultOffsets calculate_result_offsets(Span profiles, + Span curves, + const bool fill_caps) { const int total = profiles.size() * curves.size(); Array vert(total + 1); @@ -263,8 +304,8 @@ static ResultOffsets calculate_result_offsets(Span profiles, Span profiles = profile.splines(); Span curves = curve.splines(); - const ResultOffsets offsets = calculate_result_offsets(profiles, curves); + const ResultOffsets offsets = calculate_result_offsets(profiles, curves, fill_caps); if (offsets.vert.last() == 0) { return nullptr; } @@ -696,6 +737,7 @@ Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile) }; spline_extrude_to_mesh_data(info, + fill_caps, {mesh->mvert, mesh->totvert}, {mesh->medge, mesh->totedge}, {mesh->mloop, mesh->totloop}, @@ -733,7 +775,7 @@ static CurveEval get_curve_single_vert() Mesh *curve_to_wire_mesh(const CurveEval &curve) { static const CurveEval vert_curve = get_curve_single_vert(); - return curve_to_mesh_sweep(curve, vert_curve); + return curve_to_mesh_sweep(curve, vert_curve, false); } } // namespace blender::bke 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 00451946af9..be755c7269e 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 @@ -29,19 +29,25 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) { b.add_input("Curve"); b.add_input("Profile Curve"); + b.add_input("Fill Caps") + .description( + "If the profile spline is cyclic, fill the ends of the generated mesh with N-gons"); b.add_output("Mesh"); } -static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set) +static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, + const GeometrySet &profile_set, + const bool fill_caps) { + const CurveEval *curve = geometry_set.get_curve_for_read(); const CurveEval *profile_curve = profile_set.get_curve_for_read(); if (profile_curve == nullptr) { - Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read()); + Mesh *mesh = bke::curve_to_wire_mesh(*curve); geometry_set.replace_mesh(mesh); } else { - Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve); + Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps); geometry_set.replace_mesh(mesh); } } @@ -50,6 +56,7 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input("Curve"); GeometrySet profile_set = params.extract_input("Profile Curve"); + const bool fill_caps = params.extract_input("Fill Caps"); if (profile_set.has_instances()) { params.error_message_add(NodeWarningType::Error, @@ -67,7 +74,7 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_curve()) { has_curve = true; - geometry_set_curve_to_mesh(geometry_set, profile_set); + geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); } geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); }); -- cgit v1.2.3