31 auto enable_random = [](
bNode &node) {
34 auto enable_poisson = [](
bNode &node) {
38 b.add_input<
decl::Geometry>(
"Mesh").supported_type(GeometryComponent::Type::Mesh);
40 auto &distance_min =
b.add_input<
decl::Float>(
"Distance Min")
43 .make_available(enable_poisson)
45 auto &density_max =
b.add_input<
decl::Float>(
"Density Max")
56 auto &density_factor =
b.add_input<
decl::Float>(
"Density Factor")
62 .make_available(enable_poisson)
70 const bNode *node =
b.node_or_null();
71 if (node !=
nullptr) {
74 distance_min.available(
true);
75 density_max.available(
true);
76 density_factor.available(
true);
79 density.available(
true);
106 const float base_density,
114 const Span<int> corner_verts = mesh.corner_verts();
115 const Span<int3> corner_tris = mesh.corner_tris();
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]];
126 float corner_tri_density_factor = 1.0f;
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) /
134 const float area =
area_tri_v3(v0_pos, v1_pos, v2_pos);
140 corner_tri_density_factor);
142 for (
int i = 0;
i < point_amount;
i++) {
146 r_positions.
append(point_pos);
147 r_bary_coords.
append(bary_coord);
148 r_tri_indices.
append(tri_i);
155 KDTree_3d *kdtree = BLI_kdtree_3d_new(positions.
size());
158 for (
const float3 position : positions) {
159 BLI_kdtree_3d_insert(kdtree, i_point, position);
163 BLI_kdtree_3d_balance(kdtree);
170 if (minimum_distance <= 0.0f) {
178 if (elimination_mask[
i]) {
182 struct CallbackData {
185 } callback_data = {
i, elimination_mask};
187 BLI_kdtree_3d_range_search_cb(
191 [](
void *user_data,
int index,
const float * ,
float ) {
192 CallbackData &callback_data = *
static_cast<CallbackData *
>(user_data);
193 if (index != callback_data.index) {
194 callback_data.elimination_mask[index] =
true;
209 const Span<int3> corner_tris = mesh.corner_tris();
211 if (elimination_mask[
i]) {
215 const int3 &tri = corner_tris[tri_indices[
i]];
216 const float3 bary_coord = bary_coords[
i];
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]]);
222 const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
223 v2_density_factor * bary_coord.z;
226 if (
hash > probability) {
227 elimination_mask[
i] =
true;
237 for (
int i = positions.
size() - 1;
i >= 0;
i--) {
238 if (elimination_mask[
i]) {
253 switch (source_domain) {
254 case AttrDomain::Point: {
264 case AttrDomain::Corner: {
273 case AttrDomain::Face: {
299 const StringRef attribute_id = entry.key;
306 if (src.
domain == AttrDomain::Edge) {
311 attribute_id, AttrDomain::Point, output_data_type);
322struct AttributeOutputs {
333 switch (mesh.normals_domain()) {
335 const Span<int> corner_verts = mesh.corner_verts();
336 const Span<int3> corner_tris = mesh.corner_tris();
339 bke::mesh_surface_sample::sample_point_normals(
340 corner_verts, corner_tris, tri_indices, bary_coords, vert_normals, range, r_normals);
345 const Span<int> tri_faces = mesh.corner_tri_faces();
348 bke::mesh_surface_sample::sample_face_attribute(
349 tri_faces, tri_indices, face_normals, range, r_normals);
354 const Span<int3> corner_tris = mesh.corner_tris();
355 const Span<float3> corner_normals = mesh.corner_normals();
357 bke::mesh_surface_sample::sample_corner_normals(
358 corner_tris, tri_indices, bary_coords, corner_normals, range, r_normals);
371 const Span<int> corner_verts = mesh.corner_verts();
372 const Span<int3> corner_tris = mesh.corner_tris();
375 const int tri_i = tri_indices[
i];
376 const int3 &tri = corner_tris[tri_i];
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];
387 r_normals[
i] = normal;
395 for (const int i : range) {
396 r_rotations[i] = normal_to_rotation(normals[i]);
405 const AttributeOutputs &attribute_outputs,
406 const bool use_legacy_normal)
411 "id", AttrDomain::Point);
416 if (attribute_outputs.normal_id) {
418 *attribute_outputs.normal_id, AttrDomain::Point);
420 if (attribute_outputs.rotation_id) {
422 *attribute_outputs.rotation_id, AttrDomain::Point);
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);
434 if (use_legacy_normal) {
435 compute_legacy_normal_outputs(mesh, bary_coords, tri_indices,
normals.span);
438 compute_normal_outputs(mesh, bary_coords, tri_indices,
normals.span);
442 compute_rotation_output(
normals.span, rotations.span);
456 const int domain_size = mesh.
attributes().domain_size(domain);
476 mesh, density_field, selection_field);
481 const float minimum_distance,
482 const float max_density,
496 mesh, density_factor_field, selection_field);
499 mesh, density_factors, bary_coords, tri_indices, elimination_mask.
as_mutable_span());
508 const AttributeOutputs &attribute_outputs,
525 mesh, density_field, selection_field,
seed, positions, bary_coords, tri_indices);
529 const float minimum_distance =
params.get_input<
float>(
"Distance Min");
530 const float density_max =
params.get_input<
float>(
"Density Max");
535 density_factors_field,
553 pointcloud->positions_for_write().copy_from(positions);
554 point_radii.
span.fill(0.05f);
561 GeometryComponent::Type::PointCloud,
563 params.get_attribute_filter(
"Points"),
567 attributes.
remove(
"position");
571 const bool use_legacy_normal =
params.node().custom2 != 0;
573 mesh, *pointcloud, bary_coords, tri_indices, attribute_outputs, use_legacy_normal);
585 const int seed =
params.extract_input<
int>(
"Seed") * 5383843;
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));
597 geometry_set, selection_field, method,
seed, attribute_outputs,
params);
603 params.set_output(
"Points", std::move(geometry_set));
612 ntype.
ui_name =
"Distribute Points on Faces";
613 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()
bool remove(const Key &key)
ItemIterator items() const &
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 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)
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 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 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 void node_declare(NodeDeclarationBuilder &b)
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 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)
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
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)