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