Blender V5.0
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_task.hh"
6
7#include "BKE_bvhutils.hh"
8#include "BKE_geometry_set.hh"
9#include "BKE_mesh.hh"
10
12
13#include "NOD_rna_define.hh"
14
16#include "UI_resources.hh"
17
18#include "node_geometry_util.hh"
19
21
23
25{
26 b.add_input<decl::Geometry>("Geometry", "Target")
28 .supported_type({GeometryComponent::Type::Mesh, GeometryComponent::Type::PointCloud})
29 .description("Geometry to find the closest point on");
30 b.add_input<decl::Int>("Group ID")
31 .hide_value()
32 .field_on_all()
33 .description(
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(NODE_DEFAULT_INPUT_POSITION_FIELD);
38 b.add_input<decl::Int>("Sample Group ID")
39 .hide_value()
40 .supports_field()
41 .structure_type(StructureType::Dynamic);
42 b.add_output<decl::Vector>("Position").dependent_field({2, 3}).reference_pass_all();
43 b.add_output<decl::Float>("Distance").dependent_field({2, 3}).reference_pass_all();
44 b.add_output<decl::Bool>("Is Valid")
45 .dependent_field({2, 3})
47 "Whether the sampling was successful. It can fail when the sampled group is empty");
48}
49
50static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
51{
52 layout->prop(ptr, "target_element", UI_ITEM_NONE, "", ICON_NONE);
53}
54
55static void geo_proximity_init(bNodeTree * /*tree*/, bNode *node)
56{
59 node->storage = node_storage;
60}
61
62class ProximityFunction : public mf::MultiFunction {
63 private:
64 struct BVHTrees {
65 bke::BVHTreeFromMesh mesh_bvh = {};
66 bke::BVHTreeFromPointCloud pointcloud_bvh = {};
67 };
68
69 GeometrySet target_;
71 Vector<BVHTrees> bvh_trees_;
72 VectorSet<int> group_indices_;
73
74 public:
77 const Field<int> &group_id_field)
78 : target_(std::move(target)), type_(type)
79 {
80 static const mf::Signature signature = []() {
81 mf::Signature signature;
82 mf::SignatureBuilder builder{"Geometry Proximity", signature};
83 builder.single_input<float3>("Source Position");
84 builder.single_input<int>("Sample ID");
85 builder.single_output<float3>("Position", mf::ParamFlag::SupportsUnusedOutput);
86 builder.single_output<float>("Distance", mf::ParamFlag::SupportsUnusedOutput);
87 builder.single_output<bool>("Is Valid", mf::ParamFlag::SupportsUnusedOutput);
88 return signature;
89 }();
90 this->set_signature(&signature);
91
92 if (target_.has_pointcloud() && type_ == GEO_NODE_PROX_TARGET_POINTS) {
93 const PointCloud &pointcloud = *target_.get_pointcloud();
94 this->init_for_pointcloud(pointcloud, group_id_field);
95 }
96 if (target_.has_mesh()) {
97 const Mesh &mesh = *target_.get_mesh();
98 this->init_for_mesh(mesh, group_id_field);
99 }
100 }
101
102 ~ProximityFunction() override = default;
103
104 void init_for_pointcloud(const PointCloud &pointcloud, const Field<int> &group_id_field)
105 {
106 /* Compute group ids. */
107 bke::PointCloudFieldContext field_context{pointcloud};
108 FieldEvaluator field_evaluator{field_context, pointcloud.totpoint};
109 field_evaluator.add(group_id_field);
110 field_evaluator.evaluate();
111 const VArray<int> group_ids = field_evaluator.get_evaluated<int>(0);
112
113 IndexMaskMemory memory;
114 Vector<IndexMask> group_masks = IndexMask::from_group_ids(group_ids, memory, group_indices_);
115 const int groups_num = group_masks.size();
116
117 /* Construct BVH tree for each group. */
118 bvh_trees_.resize(groups_num);
120 IndexRange(groups_num),
121 512,
122 [&](const IndexRange range) {
123 for (const int group_i : range) {
124 const IndexMask &group_mask = group_masks[group_i];
125 if (group_mask.is_empty()) {
126 continue;
127 }
128 bvh_trees_[group_i].pointcloud_bvh = bke::bvhtree_from_pointcloud_get(pointcloud,
129 group_mask);
130 }
131 },
133 [&](const int group_i) { return group_masks[group_i].size(); }, pointcloud.totpoint));
134 }
135
136 void init_for_mesh(const Mesh &mesh, const Field<int> &group_id_field)
137 {
138 /* Compute group ids. */
139 const bke::AttrDomain domain = this->get_domain_on_mesh();
140 const int domain_size = mesh.attributes().domain_size(domain);
141 bke::MeshFieldContext field_context{mesh, domain};
142 FieldEvaluator field_evaluator{field_context, domain_size};
143 field_evaluator.add(group_id_field);
144 field_evaluator.evaluate();
145 const VArray<int> group_ids = field_evaluator.get_evaluated<int>(0);
146
147 IndexMaskMemory memory;
148 Vector<IndexMask> group_masks = IndexMask::from_group_ids(group_ids, memory, group_indices_);
149 const int groups_num = group_masks.size();
150
151 /* Construct BVH tree for each group. */
152 bvh_trees_.resize(groups_num);
154 IndexRange(groups_num),
155 512,
156 [&](const IndexRange range) {
157 for (const int group_i : range) {
158 const IndexMask &group_mask = group_masks[group_i];
159 if (group_mask.is_empty()) {
160 continue;
161 }
162 switch (type_) {
164 bvh_trees_[group_i].mesh_bvh = bke::bvhtree_from_mesh_verts_init(mesh, group_mask);
165 break;
167 bvh_trees_[group_i].mesh_bvh = bke::bvhtree_from_mesh_edges_init(mesh, group_mask);
168 break;
170 bvh_trees_[group_i].mesh_bvh = bke::bvhtree_from_mesh_tris_init(mesh, group_mask);
171 break;
172 }
173 }
174 },
176 [&](const int group_i) { return group_masks[group_i].size(); }, domain_size));
177 }
178
192
193 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
194 {
195 const VArray<float3> &sample_positions = params.readonly_single_input<float3>(
196 0, "Source Position");
197 const VArray<int> &sample_ids = params.readonly_single_input<int>(1, "Sample ID");
198 MutableSpan<float3> positions = params.uninitialized_single_output_if_required<float3>(
199 2, "Position");
200 MutableSpan<float> distances = params.uninitialized_single_output_if_required<float>(
201 3, "Distance");
202 MutableSpan<bool> is_valid_span = params.uninitialized_single_output_if_required<bool>(
203 4, "Is Valid");
204
205 mask.foreach_index([&](const int i) {
206 const float3 sample_position = sample_positions[i];
207 const int sample_id = sample_ids[i];
208 const int group_index = group_indices_.index_of_try(sample_id);
209 if (group_index == -1) {
210 if (!positions.is_empty()) {
211 positions[i] = float3(0, 0, 0);
212 }
213 if (!is_valid_span.is_empty()) {
214 is_valid_span[i] = false;
215 }
216 if (!distances.is_empty()) {
217 distances[i] = 0.0f;
218 }
219 return;
220 }
221 const BVHTrees &trees = bvh_trees_[group_index];
222 BVHTreeNearest nearest;
223 /* Take mesh and pointcloud bvh tree into account. The final result is the closer of the two.
224 * The first bvhtree query will set `nearest.dist_sq` which is then passed into the second
225 * query as a maximum distance. */
226 nearest.dist_sq = FLT_MAX;
227 if (trees.mesh_bvh.tree != nullptr) {
228 BLI_bvhtree_find_nearest(trees.mesh_bvh.tree,
229 sample_position,
230 &nearest,
231 trees.mesh_bvh.nearest_callback,
232 const_cast<bke::BVHTreeFromMesh *>(&trees.mesh_bvh));
233 }
234 if (trees.pointcloud_bvh.tree != nullptr) {
235 BLI_bvhtree_find_nearest(trees.pointcloud_bvh.tree,
236 sample_position,
237 &nearest,
238 trees.pointcloud_bvh.nearest_callback,
239 const_cast<bke::BVHTreeFromPointCloud *>(&trees.pointcloud_bvh));
240 }
241
242 if (!positions.is_empty()) {
243 positions[i] = nearest.co;
244 }
245 if (!is_valid_span.is_empty()) {
246 is_valid_span[i] = true;
247 }
248 if (!distances.is_empty()) {
249 distances[i] = std::sqrt(nearest.dist_sq);
250 }
251 });
252 }
253
255 {
256 ExecutionHints hints;
257 hints.min_grain_size = 512;
258 return hints;
259 }
260};
261
263{
264 GeometrySet target = params.extract_input<GeometrySet>("Target");
266
267 if (!target.has_mesh() && !target.has_pointcloud()) {
268 params.set_default_remaining_outputs();
269 return;
270 }
271
272 const NodeGeometryProximity &storage = node_storage(params.node());
273 const auto target_type = GeometryNodeProximityTargetType(storage.target_element);
274
275 Field<int> group_id_field = params.extract_input<Field<int>>("Group ID");
276 auto sample_position = params.extract_input<bke::SocketValueVariant>("Source Position");
277 auto sample_group_id = params.extract_input<bke::SocketValueVariant>("Sample Group ID");
278
279 std::string error_message;
284 std::make_shared<ProximityFunction>(
285 std::move(target), target_type, std::move(group_id_field)),
286 {&sample_position, &sample_group_id},
287 {&position, &distance, &is_valid},
288 params.user_data(),
289 error_message))
290 {
291 params.set_default_remaining_outputs();
292 params.error_message_add(NodeWarningType::Error, std::move(error_message));
293 return;
294 }
295
296 params.set_output("Position", std::move(position));
297 params.set_output("Distance", std::move(distance));
298 params.set_output("Is Valid", std::move(is_valid));
299}
300
301static void node_rna(StructRNA *srna)
302{
303 static const EnumPropertyItem target_element_items[] = {
305 "POINTS",
306 ICON_NONE,
307 "Points",
308 "Calculate the proximity to the target's points (faster than the other modes)"},
310 "EDGES",
311 ICON_NONE,
312 "Edges",
313 "Calculate the proximity to the target's edges"},
315 "FACES",
316 ICON_NONE,
317 "Faces",
318 "Calculate the proximity to the target's faces"},
319 {0, nullptr, 0, nullptr, nullptr},
320 };
321
323 "target_element",
324 "Target Geometry",
325 "Element of the target geometry to calculate the distance from",
326 target_element_items,
327 NOD_storage_enum_accessors(target_element),
329}
330
331static void node_register()
332{
333 static blender::bke::bNodeType ntype;
334
335 geo_node_type_base(&ntype, "GeometryNodeProximity", GEO_NODE_PROXIMITY);
336 ntype.ui_name = "Geometry Proximity";
337 ntype.ui_description = "Compute the closest location on the target geometry";
338 ntype.enum_name_legacy = "PROXIMITY";
342 ntype, "NodeGeometryProximity", node_free_standard_storage, node_copy_standard_storage);
343 ntype.declare = node_declare;
347
348 node_rna(ntype.rna_ext.srna);
349}
351
352} // namespace blender::nodes::node_geo_proximity_cc
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_PROXIMITY
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
@ NODE_DEFAULT_INPUT_POSITION_FIELD
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
AttributeSet attributes
static Vector< IndexMask, 4 > from_group_ids(const VArray< int > &group_ids, IndexMaskMemory &memory, VectorSet< int > &r_index_by_group_id)
int64_t size() const
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
void set_signature(const Signature *signature)
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)
float distance(VecOp< float, D >, VecOp< float, D >) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
BVHTreeFromMesh bvhtree_from_mesh_edges_init(const Mesh &mesh, const IndexMask &edges_mask)
Definition bvhutils.cc:793
BVHTreeFromMesh bvhtree_from_mesh_tris_init(const Mesh &mesh, const IndexMask &faces_mask)
Definition bvhutils.cc:784
BVHTreeFromMesh bvhtree_from_mesh_verts_init(const Mesh &mesh, const IndexMask &verts_mask)
Definition bvhutils.cc:801
BVHTreeFromPointCloud bvhtree_from_pointcloud_get(const PointCloud &pointcloud, const IndexMask &points_mask)
Definition bvhutils.cc:842
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5414
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)
bool execute_multi_function_on_value_variant(const MultiFunction &fn, const std::shared_ptr< MultiFunction > &owned_fn, const Span< SocketValueVariant * > input_values, const Span< SocketValueVariant * > output_values, GeoNodesUserData *user_data, std::string &r_error_message)
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
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, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
#define FLT_MAX
Definition stdcycles.h:14
StructRNA * srna
void * storage
BVHTree_NearestPointCallback nearest_callback
BVHTree_NearestPointCallback nearest_callback
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
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
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