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