diff options
author | Erik Abrahamsson <erik85> | 2021-08-30 07:27:35 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-08-30 07:27:35 +0300 |
commit | a71d2b26017090e1cf329631d831d11f5b84de0a (patch) | |
tree | 6633ca1f086ed77ee1880080a8e37434e78aecbf /source/blender/nodes/geometry | |
parent | 41eb33794ccd977582185159791805c84730a957 (diff) |
Geometry Nodes: Curve Fill Node
This node takes a curve geometry input and creates a filled mesh at Z=0
using a constrained Delaunay triangulation algorithm. Because of the
choice of algorithm, the results should be higher quality than the
filling for 2D curve objects.
This commit adds an initial fairly simple version of the node, but more
features may be added in the future, like transferring attributes when
necessary, or an index attribute input to break up the calculations
into smaller chunks to improve performance.
Differential Revision: https://developer.blender.org/D11846
Diffstat (limited to 'source/blender/nodes/geometry')
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc new file mode 100644 index 00000000000..d01fd35bafe --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -0,0 +1,178 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_delaunay_2d.h" +#include "BLI_double2.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" +#include "BKE_spline.hh" + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_fill_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_fill_out[] = { + {SOCK_GEOMETRY, N_("Mesh")}, + {-1, ""}, +}; + +static void geo_node_curve_fill_layout(uiLayout *layout, bContext *, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +namespace blender::nodes { + +static void geo_node_curve_fill_init(bNodeTree *, bNode *node) +{ + NodeGeometryCurveFill *data = (NodeGeometryCurveFill *)MEM_callocN(sizeof(NodeGeometryCurveFill), + __func__); + + data->mode = GEO_NODE_CURVE_FILL_MODE_TRIANGULATED; + node->storage = data; +} + +static blender::meshintersect::CDT_result<double> do_cdt(const CurveEval &curve, + const CDT_output_type output_type) +{ + Span<SplinePtr> splines = curve.splines(); + blender::meshintersect::CDT_input<double> input; + input.need_ids = false; + Array<int> offsets = curve.evaluated_point_offsets(); + input.vert.reinitialize(offsets.last()); + input.face.reinitialize(splines.size()); + + for (const int i_spline : splines.index_range()) { + const SplinePtr &spline = splines[i_spline]; + const int vert_offset = offsets[i_spline]; + + Span<float3> positions = spline->evaluated_positions(); + for (const int i : positions.index_range()) { + input.vert[vert_offset + i] = double2(positions[i].x, positions[i].y); + } + + input.face[i_spline].resize(spline->evaluated_edges_size()); + MutableSpan<int> face_verts = input.face[i_spline]; + for (const int i : IndexRange(spline->evaluated_edges_size())) { + face_verts[i] = vert_offset + i; + } + } + blender::meshintersect::CDT_result<double> result = delaunay_2d_calc(input, output_type); + return result; +} + +/* Converts the CDT result into a Mesh. */ +static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result<double> &result) +{ + int vert_len = result.vert.size(); + int edge_len = result.edge.size(); + int poly_len = result.face.size(); + int loop_len = 0; + + for (const Vector<int> &face : result.face) { + loop_len += face.size(); + } + + Mesh *mesh = BKE_mesh_new_nomain(vert_len, edge_len, 0, loop_len, poly_len); + 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}; + + for (const int i : IndexRange(result.vert.size())) { + copy_v3_v3(verts[i].co, float3((float)result.vert[i].x, (float)result.vert[i].y, 0.0f)); + } + for (const int i : IndexRange(result.edge.size())) { + edges[i].v1 = result.edge[i].first; + edges[i].v2 = result.edge[i].second; + edges[i].flag = ME_EDGEDRAW | ME_EDGERENDER; + } + int i_loop = 0; + for (const int i : IndexRange(result.face.size())) { + polys[i].loopstart = i_loop; + polys[i].totloop = result.face[i].size(); + for (const int j : result.face[i].index_range()) { + loops[i_loop].v = result.face[i][j]; + i_loop++; + } + } + + /* The delaunay triangulation doesn't seem to return all of the necessary edges, even in + * triangulation mode. */ + BKE_mesh_calc_edges(mesh, true, false); + return mesh; +} + +static Mesh *curve_fill_calculate(GeoNodeExecParams ¶ms, const CurveComponent &component) +{ + const CurveEval &curve = *component.get_for_read(); + if (curve.splines().size() == 0) { + return nullptr; + } + + const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage; + const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode; + + const CDT_output_type output_type = (mode == GEO_NODE_CURVE_FILL_MODE_NGONS) ? + CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES : + CDT_INSIDE_WITH_HOLES; + + const blender::meshintersect::CDT_result<double> results = do_cdt(curve, output_type); + return cdt_to_mesh(results); +} + +static void geo_node_curve_fill_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Mesh", GeometrySet()); + return; + } + + Mesh *mesh = curve_fill_calculate(params, + *geometry_set.get_component_for_read<CurveComponent>()); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_fill() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_FILL, "Curve Fill", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_fill_in, geo_node_curve_fill_out); + node_type_init(&ntype, blender::nodes::geo_node_curve_fill_init); + node_type_storage( + &ntype, "NodeGeometryCurveFill", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_fill_exec; + ntype.draw_buttons = geo_node_curve_fill_layout; + nodeRegisterType(&ntype); +} |