32 auto enable_random = [](
bNode &node) {
35 auto enable_poisson = [](
bNode &node) {
40 .supported_type(GeometryComponent::Type::Mesh)
41 .
description(
"Mesh on whose faces to distribute points on");
43 auto &distance_min =
b.add_input<
decl::Float>(
"Distance Min")
46 .make_available(enable_poisson)
48 auto &density_max =
b.add_input<
decl::Float>(
"Density Max")
59 auto &density_factor =
b.add_input<
decl::Float>(
"Density Factor")
65 .make_available(enable_poisson)
73 const bNode *node =
b.node_or_null();
74 if (node !=
nullptr) {
77 distance_min.available(
true);
78 density_max.available(
true);
79 density_factor.available(
true);
82 density.available(
true);
109 const float base_density,
117 const Span<int> corner_verts = mesh.corner_verts();
118 const Span<int3> corner_tris = mesh.corner_tris();
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]];
129 float corner_tri_density_factor = 1.0f;
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) /
137 const float area =
area_tri_v3(v0_pos, v1_pos, v2_pos);
143 corner_tri_density_factor);
145 for (
int i = 0;
i < point_amount;
i++) {
149 r_positions.
append(point_pos);
150 r_bary_coords.
append(bary_coord);
151 r_tri_indices.
append(tri_i);
158 KDTree_3d *kdtree = BLI_kdtree_3d_new(positions.
size());
161 for (
const float3 position : positions) {
162 BLI_kdtree_3d_insert(kdtree, i_point, position);
166 BLI_kdtree_3d_balance(kdtree);
173 if (minimum_distance <= 0.0f) {
181 if (elimination_mask[
i]) {
185 struct CallbackData {
188 } callback_data = {
i, elimination_mask};
190 BLI_kdtree_3d_range_search_cb(
194 [](
void *user_data,
int index,
const float * ,
float ) {
195 CallbackData &callback_data = *
static_cast<CallbackData *
>(user_data);
196 if (index != callback_data.index) {
197 callback_data.elimination_mask[index] =
true;
212 const Span<int3> corner_tris = mesh.corner_tris();
214 if (elimination_mask[
i]) {
218 const int3 &tri = corner_tris[tri_indices[
i]];
219 const float3 bary_coord = bary_coords[
i];
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]]);
225 const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
226 v2_density_factor * bary_coord.z;
229 if (
hash > probability) {
230 elimination_mask[
i] =
true;
240 for (
int i = positions.
size() - 1;
i >= 0;
i--) {
241 if (elimination_mask[
i]) {
256 switch (source_domain) {
257 case AttrDomain::Point: {
267 case AttrDomain::Corner: {
276 case AttrDomain::Face: {
301 for (
const int i : attributes.
names.index_range()) {
304 if (attribute_id ==
"position") {
312 if (src.
domain == AttrDomain::Edge) {
317 attribute_id, AttrDomain::Point, output_data_type);
328struct AttributeOutputs {
339 switch (mesh.normals_domain()) {
341 const Span<int> corner_verts = mesh.corner_verts();
342 const Span<int3> corner_tris = mesh.corner_tris();
345 bke::mesh_surface_sample::sample_point_normals(
346 corner_verts, corner_tris, tri_indices, bary_coords, vert_normals, range, r_normals);
351 const Span<int> tri_faces = mesh.corner_tri_faces();
354 bke::mesh_surface_sample::sample_face_attribute(
355 tri_faces, tri_indices, face_normals, range, r_normals);
360 const Span<int3> corner_tris = mesh.corner_tris();
361 const Span<float3> corner_normals = mesh.corner_normals();
363 bke::mesh_surface_sample::sample_corner_normals(
364 corner_tris, tri_indices, bary_coords, corner_normals, range, r_normals);
377 const Span<int> corner_verts = mesh.corner_verts();
378 const Span<int3> corner_tris = mesh.corner_tris();
381 const int tri_i = tri_indices[
i];
382 const int3 &tri = corner_tris[tri_i];
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];
393 r_normals[
i] = normal;
401 for (const int i : range) {
402 r_rotations[i] = normal_to_rotation(normals[i]);
411 const AttributeOutputs &attribute_outputs,
412 const bool use_legacy_normal)
417 "id", AttrDomain::Point);
422 if (attribute_outputs.normal_id) {
424 *attribute_outputs.normal_id, AttrDomain::Point);
426 if (attribute_outputs.rotation_id) {
428 *attribute_outputs.rotation_id, AttrDomain::Point);
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);
440 if (use_legacy_normal) {
441 compute_legacy_normal_outputs(mesh, bary_coords, tri_indices,
normals.span);
444 compute_normal_outputs(mesh, bary_coords, tri_indices,
normals.span);
448 compute_rotation_output(
normals.span, rotations.span);
462 const int domain_size = mesh.
attributes().domain_size(domain);
482 mesh, density_field, selection_field);
487 const float minimum_distance,
488 const float max_density,
502 mesh, density_factor_field, selection_field);
505 mesh, density_factors, bary_coords, tri_indices, elimination_mask.
as_mutable_span());
514 const AttributeOutputs &attribute_outputs,
531 mesh, density_field, selection_field,
seed, positions, bary_coords, tri_indices);
535 const float minimum_distance =
params.get_input<
float>(
"Distance Min");
536 const float density_max =
params.get_input<
float>(
"Density Max");
541 density_factors_field,
559 pointcloud->positions_for_write().copy_from(positions);
560 point_radii.
span.fill(0.05f);
567 GeometryComponent::Type::PointCloud,
569 params.get_attribute_filter(
"Points"),
574 const bool use_legacy_normal =
params.node().custom2 != 0;
576 mesh, *pointcloud, bary_coords, tri_indices, attribute_outputs, use_legacy_normal);
588 const int seed =
params.extract_input<
int>(
"Seed") * 5383843;
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));
600 geometry_set, selection_field, method,
seed, attribute_outputs,
params);
603 geometry_set.
keep_only({GeometryComponent::Type::PointCloud, GeometryComponent::Type::Edit});
606 params.set_output(
"Points", std::move(geometry_set));
615 ntype.
ui_name =
"Distribute Points on Faces";
616 ntype.
ui_description =
"Generate points spread out on the surface of a mesh";
#define NODE_CLASS_GEOMETRY
#define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES
General operations for point clouds.
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
A KD-tree for nearest neighbor search.
float area_tri_v3(const float v1[3], const float v2[3], const float v3[3])
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
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
#define NOD_REGISTER_NODE(REGISTER_FUNC)
static unsigned long seed
MutableSpan< T > as_mutable_span()
int round_probabilistic(float x)
float3 get_barycentric_coordinates()
constexpr int64_t size() const
constexpr IndexRange index_range() const
constexpr bool is_empty() const
static VArray from_span(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, AttrType data_type)
void set_selection(Field< bool > selection)
int add_with_destination(GField field, GVMutableArray dst)
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]
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)
void node_register_type(bNodeType &ntype)
void foreach_real_geometry(bke::GeometrySet &geometry, FunctionRef< void(bke::GeometrySet &geometry_set)> fn)
void debug_randomize_point_order(PointCloud *pointcloud)
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 void node_register()
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 math::Quaternion normal_to_rotation(const float3 normal)
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 void node_geo_exec(GeoNodeExecParams params)
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 void node_declare(NodeDeclarationBuilder &b)
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 ¶ms)
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)
uint32_t hash(uint32_t kx)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
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)
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
MutableVArraySpan< T > span
std::string ui_description
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeGeometryExecFunction geometry_node_execute
const char * enum_name_legacy
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeDeclareFunction declare
Vector< AttributeDomainAndType, 16 > kinds
VectorSet< StringRef, 16 > names
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)