Blender V4.3
node_geo_curve_fill.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array.hh"
6#include "BLI_delaunay_2d.hh"
8
9#include "BKE_curves.hh"
10#include "BKE_grease_pencil.hh"
11#include "BKE_instances.hh"
12#include "BKE_mesh.hh"
13
14#include "BLI_task.hh"
15
16#include "NOD_rna_define.hh"
17
18#include "UI_interface.hh"
19#include "UI_resources.hh"
20
21#include "node_geometry_util.hh"
22
24
26
28{
29 b.add_input<decl::Geometry>("Curve").supported_type(
31 b.add_input<decl::Int>("Group ID")
32 .field_on_all()
33 .hide_value()
34 .description(
35 "An index used to group curves together. Filling is done separately for each group");
36 b.add_output<decl::Geometry>("Mesh");
37}
38
39static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
40{
41 uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
42}
43
44static void node_init(bNodeTree * /*tree*/, bNode *node)
45{
46 NodeGeometryCurveFill *data = MEM_cnew<NodeGeometryCurveFill>(__func__);
47
49 node->storage = data;
50}
51
54{
55 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
56 for (const int i : range) {
57 faces[i].resize(offsets[i].size());
58 array_utils::fill_index_range<int>(faces[i], offsets[i].start());
59 }
60 });
61}
62
64 const CDT_output_type output_type)
65{
66 const OffsetIndices points_by_curve = curves.evaluated_points_by_curve();
67 const Span<float3> positions = curves.evaluated_positions();
68
69 Array<double2> positions_2d(positions.size());
70 threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
71 for (const int i : range) {
72 positions_2d[i] = double2(positions[i].x, positions[i].y);
73 }
74 });
75
76 Array<Vector<int>> faces(curves.curves_num());
77 fill_curve_vert_indices(points_by_curve, faces);
78
80 input.need_ids = false;
81 input.vert = std::move(positions_2d);
82 input.face = std::move(faces);
83
84 return delaunay_2d_calc(input, output_type);
85}
86
88 const CDT_output_type output_type,
89 const IndexMask &mask)
90{
91 const OffsetIndices points_by_curve = curves.evaluated_points_by_curve();
92 const Span<float3> positions = curves.evaluated_positions();
93
94 Array<int> offsets_data(mask.size() + 1);
95 const OffsetIndices points_by_curve_masked = offset_indices::gather_selected_offsets(
96 points_by_curve, mask, offsets_data);
97
98 Array<double2> positions_2d(points_by_curve_masked.total_size());
99 mask.foreach_index(GrainSize(1024), [&](const int src_curve, const int dst_curve) {
100 const IndexRange src_points = points_by_curve[src_curve];
101 const IndexRange dst_points = points_by_curve_masked[dst_curve];
102 for (const int i : src_points.index_range()) {
103 const int src = src_points[i];
104 const int dst = dst_points[i];
105 positions_2d[dst] = double2(positions[src].x, positions[src].y);
106 }
107 });
108
109 Array<Vector<int>> faces(points_by_curve_masked.size());
110 fill_curve_vert_indices(points_by_curve_masked, faces);
111
113 input.need_ids = false;
114 input.vert = std::move(positions_2d);
115 input.face = std::move(faces);
116
117 return delaunay_2d_calc(input, output_type);
118}
119
121 const bke::CurvesGeometry &curves,
122 const CDT_output_type output_type,
123 const Field<int> &group_index_field)
124{
125 const bke::GeometryFieldContext field_context{curves, AttrDomain::Curve};
126 fn::FieldEvaluator data_evaluator{field_context, curves.curves_num()};
127 data_evaluator.add(group_index_field);
128 data_evaluator.evaluate();
129 const VArray<int> curve_group_ids = data_evaluator.get_evaluated<int>(0);
130
131 if (curve_group_ids.is_single()) {
132 return {do_cdt(curves, output_type)};
133 }
134
135 VectorSet<int> group_indexing;
136 IndexMaskMemory mask_memory;
137 const Vector<IndexMask> group_masks = IndexMask::from_group_ids(
138 curve_group_ids, mask_memory, group_indexing);
139 const int groups_num = group_masks.size();
140
141 Array<meshintersect::CDT_result<double>> cdt_results(groups_num);
142
143 /* The grain size should be larger as each group gets smaller. */
144 const int domain_size = curve_group_ids.size();
145 const int avg_group_size = domain_size / groups_num;
146 const int grain_size = std::max(8192 / avg_group_size, 1);
147 threading::parallel_for(IndexRange(groups_num), grain_size, [&](const IndexRange range) {
148 for (const int group_index : range) {
149 const IndexMask &mask = group_masks[group_index];
150 cdt_results[group_index] = do_cdt_with_mask(curves, output_type, mask);
151 }
152 });
153
154 return cdt_results;
155}
156
157/* Converts multiple CDT results into a single Mesh. */
159{
160 /* Converting a single CDT result to a Mesh would be simple because the indices could be re-used.
161 * However, in the general case here we need to combine several CDT results into a single Mesh,
162 * which requires us to map the original indices to a new set of indices.
163 * In order to allow for parallelization when appropriate, this implementation starts by
164 * determining (for each domain) what range of indices in the final mesh data will be used for
165 * each CDT result. The index ranges are represented as offsets, which are referred to as "group
166 * offsets" to distinguish them from the other types of offsets we need to work with here.
167 * Since it's likely that most invocations will only have a single CDT result, it's important
168 * that case is made as optimal as feasible. */
169
170 Array<int> vert_groups_data(results.size() + 1);
171 Array<int> edge_groups_data(results.size() + 1);
172 Array<int> face_groups_data(results.size() + 1);
173 Array<int> loop_groups_data(results.size() + 1);
174 threading::parallel_for(results.index_range(), 1024, [&](const IndexRange results_range) {
175 for (const int i_result : results_range) {
176 const meshintersect::CDT_result<double> &result = results[i_result];
177 vert_groups_data[i_result] = result.vert.size();
178 edge_groups_data[i_result] = result.edge.size();
179 face_groups_data[i_result] = result.face.size();
180 int loop_len = 0;
181 for (const Vector<int> &face : result.face) {
182 loop_len += face.size();
183 }
184 loop_groups_data[i_result] = loop_len;
185 }
186 });
187
188 const OffsetIndices vert_groups = offset_indices::accumulate_counts_to_offsets(vert_groups_data);
189 const OffsetIndices edge_groups = offset_indices::accumulate_counts_to_offsets(edge_groups_data);
190 const OffsetIndices face_groups = offset_indices::accumulate_counts_to_offsets(face_groups_data);
191 const OffsetIndices loop_groups = offset_indices::accumulate_counts_to_offsets(loop_groups_data);
192
193 Mesh *mesh = BKE_mesh_new_nomain(vert_groups.total_size(),
194 edge_groups.total_size(),
195 face_groups.total_size(),
196 loop_groups.total_size());
197
198 MutableSpan<float3> all_positions = mesh->vert_positions_for_write();
199 MutableSpan<int2> all_edges = mesh->edges_for_write();
200 MutableSpan<int> all_face_offsets = mesh->face_offsets_for_write();
201 MutableSpan<int> all_corner_verts = mesh->corner_verts_for_write();
202
203 threading::parallel_for(results.index_range(), 1024, [&](const IndexRange results_range) {
204 for (const int i_result : results_range) {
205 const meshintersect::CDT_result<double> &result = results[i_result];
206 const IndexRange verts_range = vert_groups[i_result];
207 const IndexRange edges_range = edge_groups[i_result];
208 const IndexRange faces_range = face_groups[i_result];
209 const IndexRange loops_range = loop_groups[i_result];
210
211 MutableSpan<float3> positions = all_positions.slice(verts_range);
212 for (const int i : result.vert.index_range()) {
213 positions[i] = float3(float(result.vert[i].x), float(result.vert[i].y), 0.0f);
214 }
215
216 MutableSpan<int2> edges = all_edges.slice(edges_range);
217 for (const int i : result.edge.index_range()) {
218 edges[i] = int2(result.edge[i].first + verts_range.start(),
219 result.edge[i].second + verts_range.start());
220 }
221
222 MutableSpan<int> face_offsets = all_face_offsets.slice(faces_range);
223 MutableSpan<int> corner_verts = all_corner_verts.slice(loops_range);
224 int i_face_corner = 0;
225 for (const int i_face : result.face.index_range()) {
226 face_offsets[i_face] = i_face_corner + loops_range.start();
227 for (const int i_corner : result.face[i_face].index_range()) {
228 corner_verts[i_face_corner] = result.face[i_face][i_corner] + verts_range.start();
229 i_face_corner++;
230 }
231 }
232 }
233 });
234
235 /* The delaunay triangulation doesn't seem to return all of the necessary all_edges, even in
236 * triangulation mode. */
237 bke::mesh_calc_edges(*mesh, true, false);
238 bke::mesh_smooth_set(*mesh, false);
239
240 mesh->tag_overlapping_none();
241
242 return mesh;
243}
244
245static void curve_fill_calculate(GeometrySet &geometry_set,
246 const GeometryNodeCurveFillMode mode,
247 const Field<int> &group_index)
248{
249 const CDT_output_type output_type = (mode == GEO_NODE_CURVE_FILL_MODE_NGONS) ?
252 if (geometry_set.has_curves()) {
253 const Curves &curves_id = *geometry_set.get_curves();
254 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
255 if (curves.curves_num() > 0) {
257 curves, output_type, group_index);
258 Mesh *mesh = cdts_to_mesh(results);
259 geometry_set.replace_mesh(mesh);
260 }
261 geometry_set.replace_curves(nullptr);
262 }
263
264 if (geometry_set.has_grease_pencil()) {
265 using namespace blender::bke::greasepencil;
266 const GreasePencil &grease_pencil = *geometry_set.get_grease_pencil();
267 Vector<Mesh *> mesh_by_layer(grease_pencil.layers().size(), nullptr);
268 for (const int layer_index : grease_pencil.layers().index_range()) {
269 const Drawing *drawing = grease_pencil.get_eval_drawing(grease_pencil.layer(layer_index));
270 if (drawing == nullptr) {
271 continue;
272 }
273 const bke::CurvesGeometry &src_curves = drawing->strokes();
274 if (src_curves.curves_num() == 0) {
275 continue;
276 }
278 src_curves, output_type, group_index);
279 mesh_by_layer[layer_index] = cdts_to_mesh(results);
280 }
281 if (!mesh_by_layer.is_empty()) {
282 InstancesComponent &instances_component =
284 bke::Instances *instances = instances_component.get_for_write();
285 if (instances == nullptr) {
286 instances = new bke::Instances();
287 instances_component.replace(instances);
288 }
289 for (Mesh *mesh : mesh_by_layer) {
290 if (!mesh) {
291 /* Add an empty reference so the number of layers and instances match.
292 * This makes it easy to reconstruct the layers afterwards and keep their attributes.
293 * Although in this particular case we don't propagate the attributes. */
294 const int handle = instances->add_reference(bke::InstanceReference());
295 instances->add_instance(handle, float4x4::identity());
296 continue;
297 }
298 GeometrySet temp_set = GeometrySet::from_mesh(mesh);
299 const int handle = instances->add_reference(bke::InstanceReference{temp_set});
300 instances->add_instance(handle, float4x4::identity());
301 }
302 }
303 geometry_set.replace_grease_pencil(nullptr);
304 }
305}
306
308{
309 GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
310 Field<int> group_index = params.extract_input<Field<int>>("Group ID");
311
312 const NodeGeometryCurveFill &storage = node_storage(params.node());
313 const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode;
314
315 geometry_set.modify_geometry_sets(
316 [&](GeometrySet &geometry_set) { curve_fill_calculate(geometry_set, mode, group_index); });
317
318 params.set_output("Mesh", std::move(geometry_set));
319}
320
321static void node_rna(StructRNA *srna)
322{
323 static const EnumPropertyItem mode_items[] = {
324 {GEO_NODE_CURVE_FILL_MODE_TRIANGULATED, "TRIANGLES", 0, "Triangles", ""},
325 {GEO_NODE_CURVE_FILL_MODE_NGONS, "NGONS", 0, "N-gons", ""},
326 {0, nullptr, 0, nullptr, nullptr},
327 };
328
330 "mode",
331 "Mode",
332 "",
333 mode_items,
336}
337
338static void node_register()
339{
340 static blender::bke::bNodeType ntype;
341
342 geo_node_type_base(&ntype, GEO_NODE_FILL_CURVE, "Fill Curve", NODE_CLASS_GEOMETRY);
343
344 ntype.initfunc = node_init;
346 &ntype, "NodeGeometryCurveFill", node_free_standard_storage, node_copy_standard_storage);
347 ntype.declare = node_declare;
351
352 node_rna(ntype.rna_ext.srna);
353}
354NOD_REGISTER_NODE(node_register)
355
356} // namespace blender::nodes::node_geo_curve_fill_cc
Low-level operations for curves.
Low-level operations for grease pencil.
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
CDT_output_type
@ CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES
@ CDT_INSIDE_WITH_HOLES
GeometryNodeCurveFillMode
@ GEO_NODE_CURVE_FILL_MODE_TRIANGULATED
@ GEO_NODE_CURVE_FILL_MODE_NGONS
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_EXPAND
constexpr IndexRange index_range() const
int64_t size() const
bool is_empty() const
void replace(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const bke::CurvesGeometry & strokes() const
int add(GField field, GVArray *varray_ptr)
Definition field.cc:756
local_group_size(16, 16) .push_constant(Type b
blender::meshintersect::CDT_result< double > delaunay_2d_calc(const CDT_input< double > &input, CDT_output_type output_type)
IndexRange range
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static char faces[256]
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
static void node_init(bNodeTree *, bNode *node)
static void node_geo_exec(GeoNodeExecParams params)
static void node_declare(NodeDeclarationBuilder &b)
static Array< meshintersect::CDT_result< double > > do_group_aware_cdt(const bke::CurvesGeometry &curves, const CDT_output_type output_type, const Field< int > &group_index_field)
static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCurveFillMode mode, const Field< int > &group_index)
static void fill_curve_vert_indices(const OffsetIndices< int > offsets, MutableSpan< Vector< int > > faces)
static meshintersect::CDT_result< double > do_cdt_with_mask(const bke::CurvesGeometry &curves, const CDT_output_type output_type, const IndexMask &mask)
static Mesh * cdts_to_mesh(const Span< meshintersect::CDT_result< double > > results)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static meshintersect::CDT_result< double > do_cdt(const bke::CurvesGeometry &curves, const CDT_output_type output_type)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
CurvesGeometry geometry
StructRNA * srna
Definition RNA_types.hh:780
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
const GreasePencil * get_grease_pencil() const
const Curves * get_curves() const
void replace_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void modify_geometry_sets(ForeachSubGeometryCallback callback)
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void replace_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126