Blender V4.3
node_geo_proximity.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_math_vector.h"
6#include "BLI_task.hh"
7
8#include "BKE_bvhutils.hh"
9#include "BKE_geometry_set.hh"
10#include "BKE_mesh.hh"
11
13
14#include "NOD_rna_define.hh"
15
16#include "UI_interface.hh"
17#include "UI_resources.hh"
18
19#include "node_geometry_util.hh"
20
22
24
26{
27 b.add_input<decl::Geometry>("Geometry", "Target")
28 .only_realized_data()
30 b.add_input<decl::Int>("Group ID")
31 .hide_value()
32 .field_on_all()
34 "Splits the elements of the input geometry into groups which can be sampled "
35 "individually");
36 b.add_input<decl::Vector>("Sample Position", "Source Position")
37 .implicit_field(implicit_field_inputs::position);
38 b.add_input<decl::Int>("Sample Group ID").hide_value().supports_field();
39 b.add_output<decl::Vector>("Position").dependent_field({2, 3}).reference_pass_all();
40 b.add_output<decl::Float>("Distance").dependent_field({2, 3}).reference_pass_all();
41 b.add_output<decl::Bool>("Is Valid")
42 .dependent_field({2, 3})
43 .description(
44 "Whether the sampling was successful. It can fail when the sampled group is empty");
45}
46
47static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
48{
49 uiItemR(layout, ptr, "target_element", UI_ITEM_NONE, "", ICON_NONE);
50}
51
52static void geo_proximity_init(bNodeTree * /*tree*/, bNode *node)
53{
54 NodeGeometryProximity *node_storage = MEM_cnew<NodeGeometryProximity>(__func__);
56 node->storage = node_storage;
57}
58
60 private:
61 struct BVHTrees {
62 BVHTreeFromMesh mesh_bvh = {};
63 BVHTreeFromPointCloud pointcloud_bvh = {};
64 };
65
66 GeometrySet target_;
68 Vector<BVHTrees> bvh_trees_;
69 VectorSet<int> group_indices_;
70
71 public:
74 const Field<int> &group_id_field)
75 : target_(std::move(target)), type_(type)
76 {
77 static const mf::Signature signature = []() {
79 mf::SignatureBuilder builder{"Geometry Proximity", signature};
80 builder.single_input<float3>("Source Position");
81 builder.single_input<int>("Sample ID");
82 builder.single_output<float3>("Position", mf::ParamFlag::SupportsUnusedOutput);
83 builder.single_output<float>("Distance", mf::ParamFlag::SupportsUnusedOutput);
84 builder.single_output<bool>("Is Valid", mf::ParamFlag::SupportsUnusedOutput);
85 return signature;
86 }();
87 this->set_signature(&signature);
88
89 if (target_.has_pointcloud() && type_ == GEO_NODE_PROX_TARGET_POINTS) {
90 const PointCloud &pointcloud = *target_.get_pointcloud();
91 this->init_for_pointcloud(pointcloud, group_id_field);
92 }
93 if (target_.has_mesh()) {
94 const Mesh &mesh = *target_.get_mesh();
95 this->init_for_mesh(mesh, group_id_field);
96 }
97 }
98
100 {
101 for (BVHTrees &trees : bvh_trees_) {
102 if (trees.mesh_bvh.tree) {
103 free_bvhtree_from_mesh(&trees.mesh_bvh);
104 }
105 if (trees.pointcloud_bvh.tree) {
106 free_bvhtree_from_pointcloud(&trees.pointcloud_bvh);
107 }
108 }
109 }
110
111 void init_for_pointcloud(const PointCloud &pointcloud, const Field<int> &group_id_field)
112 {
113 /* Compute group ids. */
114 bke::PointCloudFieldContext field_context{pointcloud};
115 FieldEvaluator field_evaluator{field_context, pointcloud.totpoint};
116 field_evaluator.add(group_id_field);
117 field_evaluator.evaluate();
118 const VArray<int> group_ids = field_evaluator.get_evaluated<int>(0);
119
120 IndexMaskMemory memory;
121 Vector<IndexMask> group_masks = IndexMask::from_group_ids(group_ids, memory, group_indices_);
122 const int groups_num = group_masks.size();
123
124 /* Construct BVH tree for each group. */
125 bvh_trees_.resize(groups_num);
127 IndexRange(groups_num),
128 512,
129 [&](const IndexRange range) {
130 for (const int group_i : range) {
131 const IndexMask &group_mask = group_masks[group_i];
132 if (group_mask.is_empty()) {
133 continue;
134 }
135 BVHTreeFromPointCloud &bvh = bvh_trees_[group_i].pointcloud_bvh;
136 BKE_bvhtree_from_pointcloud_get(pointcloud, group_mask, bvh);
137 }
138 },
140 [&](const int group_i) { return group_masks[group_i].size(); }, pointcloud.totpoint));
141 }
142
143 void init_for_mesh(const Mesh &mesh, const Field<int> &group_id_field)
144 {
145 /* Compute group ids. */
146 const bke::AttrDomain domain = this->get_domain_on_mesh();
147 const int domain_size = mesh.attributes().domain_size(domain);
148 bke::MeshFieldContext field_context{mesh, domain};
149 FieldEvaluator field_evaluator{field_context, domain_size};
150 field_evaluator.add(group_id_field);
151 field_evaluator.evaluate();
152 const VArray<int> group_ids = field_evaluator.get_evaluated<int>(0);
153
154 IndexMaskMemory memory;
155 Vector<IndexMask> group_masks = IndexMask::from_group_ids(group_ids, memory, group_indices_);
156 const int groups_num = group_masks.size();
157
158 /* Construct BVH tree for each group. */
159 bvh_trees_.resize(groups_num);
161 IndexRange(groups_num),
162 512,
163 [&](const IndexRange range) {
164 for (const int group_i : range) {
165 const IndexMask &group_mask = group_masks[group_i];
166 if (group_mask.is_empty()) {
167 continue;
168 }
169 BVHTreeFromMesh &bvh = bvh_trees_[group_i].mesh_bvh;
170 switch (type_) {
172 BKE_bvhtree_from_mesh_verts_init(mesh, group_mask, bvh);
173 break;
174 }
176 BKE_bvhtree_from_mesh_edges_init(mesh, group_mask, bvh);
177 break;
178 }
180 BKE_bvhtree_from_mesh_tris_init(mesh, group_mask, bvh);
181 break;
182 }
183 }
184 }
185 },
187 [&](const int group_i) { return group_masks[group_i].size(); }, domain_size));
188 }
189
203
204 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
205 {
206 const VArray<float3> &sample_positions = params.readonly_single_input<float3>(
207 0, "Source Position");
208 const VArray<int> &sample_ids = params.readonly_single_input<int>(1, "Sample ID");
209 MutableSpan<float3> positions = params.uninitialized_single_output_if_required<float3>(
210 2, "Position");
211 MutableSpan<float> distances = params.uninitialized_single_output_if_required<float>(
212 3, "Distance");
213 MutableSpan<bool> is_valid_span = params.uninitialized_single_output_if_required<bool>(
214 4, "Is Valid");
215
216 mask.foreach_index([&](const int i) {
217 const float3 sample_position = sample_positions[i];
218 const int sample_id = sample_ids[i];
219 const int group_index = group_indices_.index_of_try(sample_id);
220 if (group_index == -1) {
221 if (!positions.is_empty()) {
222 positions[i] = float3(0, 0, 0);
223 }
224 if (!is_valid_span.is_empty()) {
225 is_valid_span[i] = false;
226 }
227 if (!distances.is_empty()) {
228 distances[i] = 0.0f;
229 }
230 return;
231 }
232 const BVHTrees &trees = bvh_trees_[group_index];
233 BVHTreeNearest nearest;
234 /* Take mesh and pointcloud bvh tree into account. The final result is the closer of the two.
235 * The first bvhtree query will set `nearest.dist_sq` which is then passed into the second
236 * query as a maximum distance. */
237 nearest.dist_sq = FLT_MAX;
238 if (trees.mesh_bvh.tree != nullptr) {
239 BLI_bvhtree_find_nearest(trees.mesh_bvh.tree,
240 sample_position,
241 &nearest,
242 trees.mesh_bvh.nearest_callback,
243 const_cast<BVHTreeFromMesh *>(&trees.mesh_bvh));
244 }
245 if (trees.pointcloud_bvh.tree != nullptr) {
246 BLI_bvhtree_find_nearest(trees.pointcloud_bvh.tree,
247 sample_position,
248 &nearest,
249 trees.pointcloud_bvh.nearest_callback,
250 const_cast<BVHTreeFromPointCloud *>(&trees.pointcloud_bvh));
251 }
252
253 if (!positions.is_empty()) {
254 positions[i] = nearest.co;
255 }
256 if (!is_valid_span.is_empty()) {
257 is_valid_span[i] = true;
258 }
259 if (!distances.is_empty()) {
260 distances[i] = std::sqrt(nearest.dist_sq);
261 }
262 });
263 }
264};
265
267{
268 GeometrySet target = params.extract_input<GeometrySet>("Target");
270
271 if (!target.has_mesh() && !target.has_pointcloud()) {
272 params.set_default_remaining_outputs();
273 return;
274 }
275
276 const NodeGeometryProximity &storage = node_storage(params.node());
277 Field<int> group_id_field = params.extract_input<Field<int>>("Group ID");
278 Field<float3> position_field = params.extract_input<Field<float3>>("Source Position");
279 Field<int> sample_id_field = params.extract_input<Field<int>>("Sample Group ID");
280
281 auto proximity_fn = std::make_unique<ProximityFunction>(
282 std::move(target), GeometryNodeProximityTargetType(storage.target_element), group_id_field);
283 auto proximity_op = FieldOperation::Create(
284 std::move(proximity_fn), {std::move(position_field), std::move(sample_id_field)});
285
286 params.set_output("Position", Field<float3>(proximity_op, 0));
287 params.set_output("Distance", Field<float>(proximity_op, 1));
288 params.set_output("Is Valid", Field<bool>(proximity_op, 2));
289}
290
291static void node_rna(StructRNA *srna)
292{
293 static const EnumPropertyItem target_element_items[] = {
295 "POINTS",
296 ICON_NONE,
297 "Points",
298 "Calculate the proximity to the target's points (faster than the other modes)"},
300 "EDGES",
301 ICON_NONE,
302 "Edges",
303 "Calculate the proximity to the target's edges"},
305 "FACES",
306 ICON_NONE,
307 "Faces",
308 "Calculate the proximity to the target's faces"},
309 {0, nullptr, 0, nullptr, nullptr},
310 };
311
313 "target_element",
314 "Target Geometry",
315 "Element of the target geometry to calculate the distance from",
316 target_element_items,
317 NOD_storage_enum_accessors(target_element),
319}
320
321static void node_register()
322{
323 static blender::bke::bNodeType ntype;
324
325 geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY);
328 &ntype, "NodeGeometryProximity", node_free_standard_storage, node_copy_standard_storage);
329 ntype.declare = node_declare;
333
334 node_rna(ntype.rna_ext.srna);
335}
337
338} // namespace blender::nodes::node_geo_proximity_cc
void free_bvhtree_from_mesh(BVHTreeFromMesh *data)
Definition bvhutils.cc:1160
void BKE_bvhtree_from_pointcloud_get(const PointCloud &pointcloud, const blender::IndexMask &points_mask, BVHTreeFromPointCloud &r_data)
Definition bvhutils.cc:1175
void free_bvhtree_from_pointcloud(BVHTreeFromPointCloud *data)
Definition bvhutils.cc:1196
void BKE_bvhtree_from_mesh_verts_init(const Mesh &mesh, const blender::IndexMask &verts_mask, BVHTreeFromMesh &r_data)
Definition bvhutils.cc:1122
void BKE_bvhtree_from_mesh_edges_init(const Mesh &mesh, const blender::IndexMask &edges_mask, BVHTreeFromMesh &r_data)
Definition bvhutils.cc:1086
void BKE_bvhtree_from_mesh_tris_init(const Mesh &mesh, const blender::IndexMask &faces_mask, BVHTreeFromMesh &r_data)
Definition bvhutils.cc:1033
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
GeometryNodeProximityTargetType
@ GEO_NODE_PROX_TARGET_EDGES
@ GEO_NODE_PROX_TARGET_POINTS
@ GEO_NODE_PROX_TARGET_FACES
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
#define UI_ITEM_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
int64_t index_of_try(const Key &key) const
int64_t size() const
void resize(const int64_t new_size)
int add(GField field, GVArray *varray_ptr)
Definition field.cc:756
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
void set_signature(const Signature *signature)
static Vector< IndexMask, 4 > from_group_ids(const VArray< int > &group_ids, IndexMaskMemory &memory, VectorSet< int > &r_index_by_group_id)
void init_for_pointcloud(const PointCloud &pointcloud, const Field< int > &group_id_field)
ProximityFunction(GeometrySet target, GeometryNodeProximityTargetType type, const Field< int > &group_id_field)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
void init_for_mesh(const Mesh &mesh, const Field< int > &group_id_field)
local_group_size(16, 16) .push_constant(Type b
IndexRange range
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
void position(const bNode &, void *r_value)
static void node_geo_exec(GeoNodeExecParams params)
static void node_declare(NodeDeclarationBuilder &b)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_rna(StructRNA *srna)
static void geo_proximity_init(bNodeTree *, bNode *node)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
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
auto individual_task_sizes(Fn &&fn, const std::optional< int64_t > full_size=std::nullopt)
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
#define FLT_MAX
Definition stdcycles.h:14
BVHTree_NearestPointCallback nearest_callback
BVHTree_NearestPointCallback nearest_callback
StructRNA * srna
Definition RNA_types.hh:780
const PointCloud * get_pointcloud() const
const Mesh * get_mesh() const
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
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