Blender V4.3
node_geo_distribute_points_on_faces.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_kdtree.h"
6#include "BLI_math_geom.h"
7#include "BLI_math_rotation.h"
8#include "BLI_noise.hh"
9#include "BLI_rand.hh"
10#include "BLI_task.hh"
11
13
14#include "BKE_attribute_math.hh"
15#include "BKE_mesh.hh"
16#include "BKE_mesh_sample.hh"
17#include "BKE_pointcloud.hh"
18
19#include "UI_interface.hh"
20#include "UI_resources.hh"
21
22#include "GEO_randomize.hh"
23
24#include "node_geometry_util.hh"
25
27
29{
30 auto enable_random = [](bNode &node) {
32 };
33 auto enable_poisson = [](bNode &node) {
35 };
36
37 b.add_input<decl::Geometry>("Mesh").supported_type(GeometryComponent::Type::Mesh);
38 b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
39 auto &distance_min = b.add_input<decl::Float>("Distance Min")
40 .min(0.0f)
42 .make_available(enable_poisson)
43 .available(false);
44 auto &density_max = b.add_input<decl::Float>("Density Max")
45 .default_value(10.0f)
46 .min(0.0f)
47 .make_available(enable_poisson)
48 .available(false);
49 auto &density = b.add_input<decl::Float>("Density")
50 .default_value(10.0f)
51 .min(0.0f)
52 .field_on_all()
53 .make_available(enable_random)
54 .available(false);
55 auto &density_factor = b.add_input<decl::Float>("Density Factor")
56 .default_value(1.0f)
57 .min(0.0f)
58 .max(1.0f)
60 .field_on_all()
61 .make_available(enable_poisson)
62 .available(false);
63 b.add_input<decl::Int>("Seed");
64
65 b.add_output<decl::Geometry>("Points").propagate_all();
66 b.add_output<decl::Vector>("Normal").field_on_all();
67 b.add_output<decl::Rotation>("Rotation").field_on_all();
68
69 const bNode *node = b.node_or_null();
70 if (node != nullptr) {
71 switch (node->custom1) {
73 distance_min.available(true);
74 density_max.available(true);
75 density_factor.available(true);
76 break;
78 density.available(true);
79 break;
80 }
81 }
82}
83
84static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
85{
86 uiItemR(layout, ptr, "distribute_method", UI_ITEM_NONE, "", ICON_NONE);
87}
88
89static void node_layout_ex(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
90{
91 uiItemR(layout, ptr, "use_legacy_normal", UI_ITEM_NONE, nullptr, ICON_NONE);
92}
93
98{
99 float quat[4];
100 vec_to_quat(quat, normal, OB_NEGZ, OB_POSY);
101 return math::normalize(math::Quaternion(quat));
102}
103
104static void sample_mesh_surface(const Mesh &mesh,
105 const float base_density,
106 const Span<float> density_factors,
107 const int seed,
108 Vector<float3> &r_positions,
109 Vector<float3> &r_bary_coords,
110 Vector<int> &r_tri_indices)
111{
112 const Span<float3> positions = mesh.vert_positions();
113 const Span<int> corner_verts = mesh.corner_verts();
114 const Span<int3> corner_tris = mesh.corner_tris();
115
116 for (const int tri_i : corner_tris.index_range()) {
117 const int3 &tri = corner_tris[tri_i];
118 const int v0_loop = tri[0];
119 const int v1_loop = tri[1];
120 const int v2_loop = tri[2];
121 const float3 &v0_pos = positions[corner_verts[v0_loop]];
122 const float3 &v1_pos = positions[corner_verts[v1_loop]];
123 const float3 &v2_pos = positions[corner_verts[v2_loop]];
124
125 float corner_tri_density_factor = 1.0f;
126 if (!density_factors.is_empty()) {
127 const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]);
128 const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]);
129 const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]);
130 corner_tri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) /
131 3.0f;
132 }
133 const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
134
135 const int corner_tri_seed = noise::hash(tri_i, seed);
136 RandomNumberGenerator corner_tri_rng(corner_tri_seed);
137
138 const int point_amount = corner_tri_rng.round_probabilistic(area * base_density *
139 corner_tri_density_factor);
140
141 for (int i = 0; i < point_amount; i++) {
142 const float3 bary_coord = corner_tri_rng.get_barycentric_coordinates();
143 float3 point_pos;
144 interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coord);
145 r_positions.append(point_pos);
146 r_bary_coords.append(bary_coord);
147 r_tri_indices.append(tri_i);
148 }
149 }
150}
151
152BLI_NOINLINE static KDTree_3d *build_kdtree(Span<float3> positions)
153{
154 KDTree_3d *kdtree = BLI_kdtree_3d_new(positions.size());
155
156 int i_point = 0;
157 for (const float3 position : positions) {
158 BLI_kdtree_3d_insert(kdtree, i_point, position);
159 i_point++;
160 }
161
162 BLI_kdtree_3d_balance(kdtree);
163 return kdtree;
164}
165
167 Span<float3> positions, const float minimum_distance, MutableSpan<bool> elimination_mask)
168{
169 if (minimum_distance <= 0.0f) {
170 return;
171 }
172
173 KDTree_3d *kdtree = build_kdtree(positions);
174 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); });
175
176 for (const int i : positions.index_range()) {
177 if (elimination_mask[i]) {
178 continue;
179 }
180
181 struct CallbackData {
182 int index;
183 MutableSpan<bool> elimination_mask;
184 } callback_data = {i, elimination_mask};
185
186 BLI_kdtree_3d_range_search_cb(
187 kdtree,
188 positions[i],
189 minimum_distance,
190 [](void *user_data, int index, const float * /*co*/, float /*dist_sq*/) {
191 CallbackData &callback_data = *static_cast<CallbackData *>(user_data);
192 if (index != callback_data.index) {
193 callback_data.elimination_mask[index] = true;
194 }
195 return true;
196 },
197 &callback_data);
198 }
199}
200
202 const Mesh &mesh,
203 const Span<float> density_factors,
204 const Span<float3> bary_coords,
205 const Span<int> tri_indices,
206 const MutableSpan<bool> elimination_mask)
207{
208 const Span<int3> corner_tris = mesh.corner_tris();
209 for (const int i : bary_coords.index_range()) {
210 if (elimination_mask[i]) {
211 continue;
212 }
213
214 const int3 &tri = corner_tris[tri_indices[i]];
215 const float3 bary_coord = bary_coords[i];
216
217 const float v0_density_factor = std::max(0.0f, density_factors[tri[0]]);
218 const float v1_density_factor = std::max(0.0f, density_factors[tri[1]]);
219 const float v2_density_factor = std::max(0.0f, density_factors[tri[2]]);
220
221 const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
222 v2_density_factor * bary_coord.z;
223
224 const float hash = noise::hash_float_to_float(bary_coord);
225 if (hash > probability) {
226 elimination_mask[i] = true;
227 }
228 }
229}
230
231BLI_NOINLINE static void eliminate_points_based_on_mask(const Span<bool> elimination_mask,
232 Vector<float3> &positions,
233 Vector<float3> &bary_coords,
234 Vector<int> &tri_indices)
235{
236 for (int i = positions.size() - 1; i >= 0; i--) {
237 if (elimination_mask[i]) {
238 positions.remove_and_reorder(i);
239 bary_coords.remove_and_reorder(i);
240 tri_indices.remove_and_reorder(i);
241 }
242 }
243}
244
246 const Span<float3> bary_coords,
247 const Span<int> tri_indices,
248 const AttrDomain source_domain,
249 const GVArray &source_data,
250 GMutableSpan output_data)
251{
252 switch (source_domain) {
253 case AttrDomain::Point: {
255 mesh.corner_tris(),
256 tri_indices,
257 bary_coords,
259 IndexMask(output_data.size()),
260 output_data);
261 break;
262 }
263 case AttrDomain::Corner: {
265 tri_indices,
266 bary_coords,
268 IndexMask(output_data.size()),
269 output_data);
270 break;
271 }
272 case AttrDomain::Face: {
274 tri_indices,
276 IndexMask(output_data.size()),
277 output_data);
278 break;
279 }
280 default: {
281 /* Not supported currently. */
282 return;
283 }
284 }
285}
286
288 const Mesh &mesh,
289 const Map<StringRef, AttributeKind> &attributes,
290 PointCloud &points,
291 const Span<float3> bary_coords,
292 const Span<int> tri_indices)
293{
294 const AttributeAccessor mesh_attributes = mesh.attributes();
295 MutableAttributeAccessor point_attributes = points.attributes_for_write();
296
297 for (MapItem<StringRef, AttributeKind> entry : attributes.items()) {
298 const StringRef attribute_id = entry.key;
299 const eCustomDataType output_data_type = entry.value.data_type;
300
301 GAttributeReader src = mesh_attributes.lookup(attribute_id);
302 if (!src) {
303 continue;
304 }
305 if (src.domain == AttrDomain::Edge) {
306 continue;
307 }
308
310 attribute_id, AttrDomain::Point, output_data_type);
311 if (!dst) {
312 continue;
313 }
314
315 interpolate_attribute(mesh, bary_coords, tri_indices, src.domain, src.varray, dst.span);
316 dst.finish();
317 }
318}
319
320namespace {
321struct AttributeOutputs {
322 std::optional<std::string> normal_id;
323 std::optional<std::string> rotation_id;
324};
325} // namespace
326
327static void compute_normal_outputs(const Mesh &mesh,
328 const Span<float3> bary_coords,
329 const Span<int> tri_indices,
330 MutableSpan<float3> r_normals)
331{
332 switch (mesh.normals_domain()) {
334 const Span<int> corner_verts = mesh.corner_verts();
335 const Span<int3> corner_tris = mesh.corner_tris();
336 const Span<float3> vert_normals = mesh.vert_normals();
337 threading::parallel_for(bary_coords.index_range(), 512, [&](const IndexRange range) {
338 bke::mesh_surface_sample::sample_point_normals(
339 corner_verts, corner_tris, tri_indices, bary_coords, vert_normals, range, r_normals);
340 });
341 break;
342 }
344 const Span<int> tri_faces = mesh.corner_tri_faces();
345 VArray<float3> face_normals = VArray<float3>::ForSpan(mesh.face_normals());
346 threading::parallel_for(bary_coords.index_range(), 512, [&](const IndexRange range) {
347 bke::mesh_surface_sample::sample_face_attribute(
348 tri_faces, tri_indices, face_normals, range, r_normals);
349 });
350 break;
351 }
353 const Span<int3> corner_tris = mesh.corner_tris();
354 const Span<float3> corner_normals = mesh.corner_normals();
355 threading::parallel_for(bary_coords.index_range(), 512, [&](const IndexRange range) {
356 bke::mesh_surface_sample::sample_corner_normals(
357 corner_tris, tri_indices, bary_coords, corner_normals, range, r_normals);
358 });
359 break;
360 }
361 }
362}
363
364static void compute_legacy_normal_outputs(const Mesh &mesh,
365 const Span<float3> bary_coords,
366 const Span<int> tri_indices,
367 MutableSpan<float3> r_normals)
368{
369 const Span<float3> positions = mesh.vert_positions();
370 const Span<int> corner_verts = mesh.corner_verts();
371 const Span<int3> corner_tris = mesh.corner_tris();
372
373 for (const int i : bary_coords.index_range()) {
374 const int tri_i = tri_indices[i];
375 const int3 &tri = corner_tris[tri_i];
376
377 const int v0_index = corner_verts[tri[0]];
378 const int v1_index = corner_verts[tri[1]];
379 const int v2_index = corner_verts[tri[2]];
380 const float3 v0_pos = positions[v0_index];
381 const float3 v1_pos = positions[v1_index];
382 const float3 v2_pos = positions[v2_index];
383
384 float3 normal;
385 normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
386 r_normals[i] = normal;
387 }
388}
389
390static void compute_rotation_output(const Span<float3> normals,
392{
393 threading::parallel_for(normals.index_range(), 512, [&](const IndexRange range) {
394 for (const int i : range) {
395 r_rotations[i] = normal_to_rotation(normals[i]);
396 }
397 });
398}
399
401 PointCloud &points,
402 const Span<float3> bary_coords,
403 const Span<int> tri_indices,
404 const AttributeOutputs &attribute_outputs,
405 const bool use_legacy_normal)
406{
407 MutableAttributeAccessor point_attributes = points.attributes_for_write();
408
410 "id", AttrDomain::Point);
411
414
415 if (attribute_outputs.normal_id) {
416 normals = point_attributes.lookup_or_add_for_write_only_span<float3>(
417 *attribute_outputs.normal_id, AttrDomain::Point);
418 }
419 if (attribute_outputs.rotation_id) {
420 rotations = point_attributes.lookup_or_add_for_write_only_span<math::Quaternion>(
421 *attribute_outputs.rotation_id, AttrDomain::Point);
422 }
423
424 threading::parallel_for(bary_coords.index_range(), 1024, [&](const IndexRange range) {
425 for (const int i : range) {
426 const int tri_i = tri_indices[i];
427 const float3 &bary_coord = bary_coords[i];
428 ids.span[i] = noise::hash(noise::hash_float(bary_coord), tri_i);
429 }
430 });
431
432 if (normals) {
433 if (use_legacy_normal) {
434 compute_legacy_normal_outputs(mesh, bary_coords, tri_indices, normals.span);
435 }
436 else {
437 compute_normal_outputs(mesh, bary_coords, tri_indices, normals.span);
438 }
439
440 if (rotations) {
441 compute_rotation_output(normals.span, rotations.span);
442 }
443 }
444
445 ids.finish();
446 normals.finish();
447 rotations.finish();
448}
449
451 const Field<float> &density_field,
452 const Field<bool> &selection_field)
453{
454 const AttrDomain domain = AttrDomain::Corner;
455 const int domain_size = mesh.attributes().domain_size(domain);
456 Array<float> densities(domain_size, 0.0f);
457
458 const bke::MeshFieldContext field_context{mesh, domain};
459 fn::FieldEvaluator evaluator{field_context, domain_size};
460 evaluator.set_selection(selection_field);
461 evaluator.add_with_destination(density_field, densities.as_mutable_span());
462 evaluator.evaluate();
463 return densities;
464}
465
466static void distribute_points_random(const Mesh &mesh,
467 const Field<float> &density_field,
468 const Field<bool> &selection_field,
469 const int seed,
470 Vector<float3> &positions,
471 Vector<float3> &bary_coords,
472 Vector<int> &tri_indices)
473{
475 mesh, density_field, selection_field);
476 sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, tri_indices);
477}
478
479static void distribute_points_poisson_disk(const Mesh &mesh,
480 const float minimum_distance,
481 const float max_density,
482 const Field<float> &density_factor_field,
483 const Field<bool> &selection_field,
484 const int seed,
485 Vector<float3> &positions,
486 Vector<float3> &bary_coords,
487 Vector<int> &tri_indices)
488{
489 sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, tri_indices);
490
491 Array<bool> elimination_mask(positions.size(), false);
492 update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask);
493
495 mesh, density_factor_field, selection_field);
496
498 mesh, density_factors, bary_coords, tri_indices, elimination_mask.as_mutable_span());
499
500 eliminate_points_based_on_mask(elimination_mask.as_span(), positions, bary_coords, tri_indices);
501}
502
504 const Field<bool> selection_field,
506 const int seed,
507 const AttributeOutputs &attribute_outputs,
509{
510 if (!geometry_set.has_mesh()) {
511 return;
512 }
513
514 const Mesh &mesh = *geometry_set.get_mesh();
515
516 Vector<float3> positions;
517 Vector<float3> bary_coords;
518 Vector<int> tri_indices;
519
520 switch (method) {
522 const Field<float> density_field = params.get_input<Field<float>>("Density");
524 mesh, density_field, selection_field, seed, positions, bary_coords, tri_indices);
525 break;
526 }
528 const float minimum_distance = params.get_input<float>("Distance Min");
529 const float density_max = params.get_input<float>("Density Max");
530 const Field<float> density_factors_field = params.get_input<Field<float>>("Density Factor");
532 minimum_distance,
533 density_max,
534 density_factors_field,
535 selection_field,
536 seed,
537 positions,
538 bary_coords,
539 tri_indices);
540 break;
541 }
542 }
543
544 if (positions.is_empty()) {
545 return;
546 }
547
548 PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
549 bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
551 point_attributes.lookup_or_add_for_write_only_span<float>("radius", AttrDomain::Point);
552 pointcloud->positions_for_write().copy_from(positions);
553 point_radii.span.fill(0.05f);
554 point_radii.finish();
555
556 geometry_set.replace_pointcloud(pointcloud);
557
559 geometry_set.gather_attributes_for_propagation({GeometryComponent::Type::Mesh},
560 GeometryComponent::Type::PointCloud,
561 false,
562 params.get_attribute_filter("Points"),
563 attributes);
564
565 /* Position is set separately. */
566 attributes.remove("position");
567
568 propagate_existing_attributes(mesh, attributes, *pointcloud, bary_coords, tri_indices);
569
570 const bool use_legacy_normal = params.node().custom2 != 0;
572 mesh, *pointcloud, bary_coords, tri_indices, attribute_outputs, use_legacy_normal);
573
574 geometry::debug_randomize_point_order(pointcloud);
575}
576
578{
579 GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
580
582 params.node().custom1);
583
584 const int seed = params.get_input<int>("Seed") * 5383843;
585 const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
586
587 AttributeOutputs attribute_outputs;
588 attribute_outputs.rotation_id = params.get_output_anonymous_attribute_id_if_needed("Rotation");
589 attribute_outputs.normal_id = params.get_output_anonymous_attribute_id_if_needed(
590 "Normal", bool(attribute_outputs.rotation_id));
591
592 lazy_threading::send_hint();
593
594 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
596 geometry_set, selection_field, method, seed, attribute_outputs, params);
597 /* Keep instances because the original geometry set may contain instances that are processed as
598 * well. */
599 geometry_set.keep_only_during_modify({GeometryComponent::Type::PointCloud});
600 });
601
602 params.set_output("Points", std::move(geometry_set));
603}
604
605static void node_register()
606{
607 static blender::bke::bNodeType ntype;
608
609 geo_node_type_base(&ntype,
610 GEO_NODE_DISTRIBUTE_POINTS_ON_FACES,
611 "Distribute Points on Faces",
613 blender::bke::node_type_size(&ntype, 170, 100, 320);
614 ntype.declare = node_declare;
619}
620NOD_REGISTER_NODE(node_register)
621
622} // namespace blender::nodes::node_geo_distribute_points_on_faces_cc
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
General operations for point clouds.
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
#define BLI_NOINLINE
A KD-tree for nearest neighbor search.
float area_tri_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:98
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:39
void vec_to_quat(float q[4], const float vec[3], short axis, short upflag)
void interp_v3_v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], const float w[3])
#define BLI_SCOPED_DEFER(function_to_defer)
GeometryNodeDistributePointsOnFacesMode
@ GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM
@ GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON
@ OB_NEGZ
@ OB_POSY
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_DISTANCE
Definition RNA_types.hh:159
@ PROP_FACTOR
Definition RNA_types.hh:154
#define UI_ITEM_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
static unsigned long seed
Definition btSoftBody.h:39
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
int round_probabilistic(float x)
Definition rand.cc:383
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
static VArray ForSpan(Span< T > values)
void remove_and_reorder(const int64_t index)
void append(const T &value)
MutableSpan< T > as_mutable_span()
GAttributeReader lookup(const StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
void set_selection(Field< bool > selection)
Definition FN_field.hh:385
void make_available(bNode &node) const
local_group_size(16, 16) .push_constant(Type b
OperationNode * node
GPU_SHADER_INTERFACE_INFO(depth_2d_update_iface, "").smooth(Type fragColor push_constant(Type::VEC2, "extent") .push_constant(Type source_data
static float normals[][3]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void sample_point_attribute(Span< int > corner_verts, Span< int3 > corner_tris, Span< int > tri_indices, Span< float3 > bary_coords, const GVArray &src, const IndexMask &mask, GMutableSpan dst)
void sample_corner_attribute(Span< int3 > corner_tris, Span< int > tri_indices, Span< float3 > bary_coords, const GVArray &src, const IndexMask &mask, GMutableSpan dst)
void sample_face_attribute(Span< int > corner_tri_faces, Span< int > tri_indices, const GVArray &src, const IndexMask &mask, GMutableSpan dst)
void node_type_size(bNodeType *ntype, int width, int minwidth, int maxwidth)
Definition node.cc:4602
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
static BLI_NOINLINE void update_elimination_mask_for_close_points(Span< float3 > positions, const float minimum_distance, MutableSpan< bool > elimination_mask)
static BLI_NOINLINE KDTree_3d * build_kdtree(Span< float3 > positions)
static BLI_NOINLINE void interpolate_attribute(const Mesh &mesh, const Span< float3 > bary_coords, const Span< int > tri_indices, const AttrDomain source_domain, const GVArray &source_data, GMutableSpan output_data)
static void distribute_points_poisson_disk(const Mesh &mesh, const float minimum_distance, const float max_density, const Field< float > &density_factor_field, const Field< bool > &selection_field, const int seed, Vector< float3 > &positions, Vector< float3 > &bary_coords, Vector< int > &tri_indices)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static BLI_NOINLINE void compute_attribute_outputs(const Mesh &mesh, PointCloud &points, const Span< float3 > bary_coords, const Span< int > tri_indices, const AttributeOutputs &attribute_outputs, const bool use_legacy_normal)
static void compute_normal_outputs(const Mesh &mesh, const Span< float3 > bary_coords, const Span< int > tri_indices, MutableSpan< float3 > r_normals)
static BLI_NOINLINE void update_elimination_mask_based_on_density_factors(const Mesh &mesh, const Span< float > density_factors, const Span< float3 > bary_coords, const Span< int > tri_indices, const MutableSpan< bool > elimination_mask)
static void node_layout_ex(uiLayout *layout, bContext *, PointerRNA *ptr)
static void sample_mesh_surface(const Mesh &mesh, const float base_density, const Span< float > density_factors, const int seed, Vector< float3 > &r_positions, Vector< float3 > &r_bary_coords, Vector< int > &r_tri_indices)
static Array< float > calc_full_density_factors_with_selection(const Mesh &mesh, const Field< float > &density_field, const Field< bool > &selection_field)
static void compute_rotation_output(const Span< float3 > normals, MutableSpan< math::Quaternion > r_rotations)
static BLI_NOINLINE void propagate_existing_attributes(const Mesh &mesh, const Map< StringRef, AttributeKind > &attributes, PointCloud &points, const Span< float3 > bary_coords, const Span< int > tri_indices)
static void point_distribution_calculate(GeometrySet &geometry_set, const Field< bool > selection_field, const GeometryNodeDistributePointsOnFacesMode method, const int seed, const AttributeOutputs &attribute_outputs, const GeoNodeExecParams &params)
static BLI_NOINLINE void eliminate_points_based_on_mask(const Span< bool > elimination_mask, Vector< float3 > &positions, Vector< float3 > &bary_coords, Vector< int > &tri_indices)
static void compute_legacy_normal_outputs(const Mesh &mesh, const Span< float3 > bary_coords, const Span< int > tri_indices, MutableSpan< float3 > r_normals)
static void distribute_points_random(const Mesh &mesh, const Field< float > &density_field, const Field< bool > &selection_field, const int seed, Vector< float3 > &positions, Vector< float3 > &bary_coords, Vector< int > &tri_indices)
float hash_float_to_float(float k)
Definition noise.cc:188
uint32_t hash(uint32_t kx)
Definition noise.cc:72
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
std::optional< std::string > rotation_id
std::optional< std::string > normal_id
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
#define hash
Definition noise.c:154
#define min(a, b)
Definition sort.c:32
void gather_attributes_for_propagation(Span< GeometryComponent::Type > component_types, GeometryComponent::Type dst_component_type, bool include_instances, const AttributeFilter &attribute_filter, Map< StringRef, AttributeKind > &r_attributes) const
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void keep_only_during_modify(Span< GeometryComponent::Type > component_types)
const Mesh * get_mesh() const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
Defines a node type.
Definition BKE_node.hh:218
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:240
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