Blender V4.3
node_geo_sample_nearest.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
6
7#include "BKE_bvhutils.hh"
8#include "BKE_mesh.hh"
9
10#include "NOD_rna_define.hh"
11
12#include "UI_interface.hh"
13#include "UI_resources.hh"
14
15#include "RNA_enum_types.hh"
16
17#include "node_geometry_util.hh"
18
19namespace blender::nodes {
20
22 const VArray<float3> &positions,
23 const IndexMask &mask,
24 const MutableSpan<int> r_indices,
25 const MutableSpan<float> r_distances_sq,
26 const MutableSpan<float3> r_positions)
27{
28 BLI_assert(positions.size() >= r_indices.size());
29 BLI_assert(positions.size() >= r_distances_sq.size());
30 BLI_assert(positions.size() >= r_positions.size());
31
32 mask.foreach_index([&](const int i) {
33 BVHTreeNearest nearest;
34 nearest.index = -1;
35 nearest.dist_sq = FLT_MAX;
36 const float3 position = positions[i];
38 tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
39 if (!r_indices.is_empty()) {
40 r_indices[i] = nearest.index;
41 }
42 if (!r_distances_sq.is_empty()) {
43 r_distances_sq[i] = nearest.dist_sq;
44 }
45 if (!r_positions.is_empty()) {
46 r_positions[i] = nearest.co;
47 }
48 });
49}
50
51} // namespace blender::nodes
52
54
56{
57 b.add_input<decl::Geometry>("Geometry")
59 b.add_input<decl::Vector>("Sample Position").implicit_field(implicit_field_inputs::position);
60 b.add_output<decl::Int>("Index").dependent_field({1});
61}
62
63static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
64{
65 uiItemR(layout, ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
66}
67
68static void node_init(bNodeTree * /*tree*/, bNode *node)
69{
70 node->custom1 = CD_PROP_FLOAT;
71 node->custom2 = int(AttrDomain::Point);
72}
73
74static void get_closest_pointcloud_points(const PointCloud &pointcloud,
75 const VArray<float3> &positions,
76 const IndexMask &mask,
77 MutableSpan<int> r_indices,
78 MutableSpan<float> r_distances_sq)
79{
80 BLI_assert(positions.size() >= r_indices.size());
81 BLI_assert(pointcloud.totpoint > 0);
82
83 BVHTreeFromPointCloud tree_data;
84 BKE_bvhtree_from_pointcloud_get(pointcloud, IndexMask(pointcloud.totpoint), tree_data);
85 if (tree_data.tree == nullptr) {
86 r_indices.fill(0);
87 r_distances_sq.fill(0.0f);
88 return;
89 }
90
91 mask.foreach_index([&](const int i) {
92 BVHTreeNearest nearest;
93 nearest.index = -1;
94 nearest.dist_sq = FLT_MAX;
95 const float3 position = positions[i];
97 tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
98 r_indices[i] = nearest.index;
99 if (!r_distances_sq.is_empty()) {
100 r_distances_sq[i] = nearest.dist_sq;
101 }
102 });
103
105}
106
107static void get_closest_mesh_points(const Mesh &mesh,
108 const VArray<float3> &positions,
109 const IndexMask &mask,
110 const MutableSpan<int> r_point_indices,
111 const MutableSpan<float> r_distances_sq,
112 const MutableSpan<float3> r_positions)
113{
114 BLI_assert(mesh.verts_num > 0);
115 BVHTreeFromMesh tree_data;
116 BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2);
117 get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions);
118 free_bvhtree_from_mesh(&tree_data);
119}
120
121static void get_closest_mesh_edges(const Mesh &mesh,
122 const VArray<float3> &positions,
123 const IndexMask &mask,
124 const MutableSpan<int> r_edge_indices,
125 const MutableSpan<float> r_distances_sq,
126 const MutableSpan<float3> r_positions)
127{
128 BLI_assert(mesh.edges_num > 0);
129 BVHTreeFromMesh tree_data;
130 BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2);
131 get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions);
132 free_bvhtree_from_mesh(&tree_data);
133}
134
135static void get_closest_mesh_tris(const Mesh &mesh,
136 const VArray<float3> &positions,
137 const IndexMask &mask,
138 const MutableSpan<int> r_tri_indices,
139 const MutableSpan<float> r_distances_sq,
140 const MutableSpan<float3> r_positions)
141{
142 BLI_assert(mesh.faces_num > 0);
143 BVHTreeFromMesh tree_data;
145 get_closest_in_bvhtree(tree_data, positions, mask, r_tri_indices, r_distances_sq, r_positions);
146 free_bvhtree_from_mesh(&tree_data);
147}
148
149static void get_closest_mesh_faces(const Mesh &mesh,
150 const VArray<float3> &positions,
151 const IndexMask &mask,
152 const MutableSpan<int> r_face_indices,
153 const MutableSpan<float> r_distances_sq,
154 const MutableSpan<float3> r_positions)
155{
156 BLI_assert(mesh.faces_num > 0);
157
158 Array<int> tri_indices(positions.size());
159 get_closest_mesh_tris(mesh, positions, mask, tri_indices, r_distances_sq, r_positions);
160
161 const Span<int> tri_faces = mesh.corner_tri_faces();
162
163 mask.foreach_index([&](const int i) { r_face_indices[i] = tri_faces[tri_indices[i]]; });
164}
165
166/* The closest corner is defined to be the closest corner on the closest face. */
167static void get_closest_mesh_corners(const Mesh &mesh,
168 const VArray<float3> &positions,
169 const IndexMask &mask,
170 const MutableSpan<int> r_corner_indices,
171 const MutableSpan<float> r_distances_sq,
172 const MutableSpan<float3> r_positions)
173{
174 const Span<float3> vert_positions = mesh.vert_positions();
175 const OffsetIndices faces = mesh.faces();
176 const Span<int> corner_verts = mesh.corner_verts();
177
178 BLI_assert(mesh.corners_num > 0);
179 Array<int> face_indices(positions.size());
180 get_closest_mesh_faces(mesh, positions, mask, face_indices, {}, {});
181
182 mask.foreach_index([&](const int i) {
183 const float3 position = positions[i];
184 const int face_index = face_indices[i];
185
186 /* Find the closest vertex in the face. */
187 float min_distance_sq = FLT_MAX;
188 int closest_vert_index = 0;
189 int closest_loop_index = 0;
190 for (const int loop_index : faces[face_index]) {
191 const int vertex_index = corner_verts[loop_index];
192 const float distance_sq = math::distance_squared(position, vert_positions[vertex_index]);
193 if (distance_sq < min_distance_sq) {
194 min_distance_sq = distance_sq;
195 closest_loop_index = loop_index;
196 closest_vert_index = vertex_index;
197 }
198 }
199 if (!r_corner_indices.is_empty()) {
200 r_corner_indices[i] = closest_loop_index;
201 }
202 if (!r_positions.is_empty()) {
203 r_positions[i] = vert_positions[closest_vert_index];
204 }
205 if (!r_distances_sq.is_empty()) {
206 r_distances_sq[i] = min_distance_sq;
207 }
208 });
209}
210
211static bool component_is_available(const GeometrySet &geometry,
212 const GeometryComponent::Type type,
213 const AttrDomain domain)
214{
215 if (!geometry.has(type)) {
216 return false;
217 }
218 const GeometryComponent &component = *geometry.get_component(type);
219 return component.attribute_domain_size(domain) != 0;
220}
221
223 const AttrDomain domain)
224{
225 /* Choose the other component based on a consistent order, rather than some more complicated
226 * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */
227 static const Array<GeometryComponent::Type> supported_types = {
229 for (const GeometryComponent::Type src_type : supported_types) {
230 if (component_is_available(geometry, src_type, domain)) {
231 return geometry.get_component(src_type);
232 }
233 }
234
235 return nullptr;
236}
237
239 GeometrySet source_;
240 AttrDomain domain_;
241
242 const GeometryComponent *src_component_;
243
244 mf::Signature signature_;
245
246 public:
247 SampleNearestFunction(GeometrySet geometry, AttrDomain domain)
248 : source_(std::move(geometry)), domain_(domain)
249 {
250 source_.ensure_owns_direct_data();
251 this->src_component_ = find_source_component(source_, domain_);
252
253 mf::SignatureBuilder builder{"Sample Nearest", signature_};
254 builder.single_input<float3>("Position");
255 builder.single_output<int>("Index");
256 this->set_signature(&signature_);
257 }
258
259 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
260 {
261 const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position");
262 MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Index");
263 if (!src_component_) {
264 index_mask::masked_fill(indices, 0, mask);
265 return;
266 }
267
268 switch (src_component_->type()) {
270 const MeshComponent &component = *static_cast<const MeshComponent *>(src_component_);
271 const Mesh &mesh = *component.get();
272 switch (domain_) {
273 case AttrDomain::Point:
274 get_closest_mesh_points(mesh, positions, mask, indices, {}, {});
275 break;
276 case AttrDomain::Edge:
277 get_closest_mesh_edges(mesh, positions, mask, indices, {}, {});
278 break;
279 case AttrDomain::Face:
280 get_closest_mesh_faces(mesh, positions, mask, indices, {}, {});
281 break;
282 case AttrDomain::Corner:
283 get_closest_mesh_corners(mesh, positions, mask, indices, {}, {});
284 break;
285 default:
286 break;
287 }
288 break;
289 }
291 const PointCloudComponent &component = *static_cast<const PointCloudComponent *>(
292 src_component_);
293 const PointCloud &points = *component.get();
294 get_closest_pointcloud_points(points, positions, mask, indices, {});
295 break;
296 }
297 default:
298 break;
299 }
300 }
301};
302
304{
305 GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
306 const AttrDomain domain = AttrDomain(params.node().custom2);
307 if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) {
308 params.error_message_add(NodeWarningType::Error,
309 TIP_("The source geometry must contain a mesh or a point cloud"));
310 params.set_default_remaining_outputs();
311 return;
312 }
313
314 Field<float3> positions = params.extract_input<Field<float3>>("Sample Position");
315 auto fn = std::make_shared<SampleNearestFunction>(std::move(geometry), domain);
316 auto op = FieldOperation::Create(std::move(fn), {std::move(positions)});
317 params.set_output<Field<int>>("Index", Field<int>(std::move(op)));
318}
319
320static void node_rna(StructRNA *srna)
321{
323 "domain",
324 "Domain",
325 "",
328 int(AttrDomain::Point));
329}
330
331static void node_register()
332{
333 static blender::bke::bNodeType ntype;
334
335 geo_node_type_base(&ntype, GEO_NODE_SAMPLE_NEAREST, "Sample Nearest", NODE_CLASS_GEOMETRY);
336 ntype.initfunc = node_init;
337 ntype.declare = node_declare;
341
342 node_rna(ntype.rna_ext.srna);
343}
345
346} // namespace blender::nodes::node_geo_sample_nearest_cc
void free_bvhtree_from_mesh(BVHTreeFromMesh *data)
Definition bvhutils.cc:1160
BVHTree * BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data, const Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition bvhutils.cc:899
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
@ BVHTREE_FROM_CORNER_TRIS
@ BVHTREE_FROM_EDGES
@ BVHTREE_FROM_VERTS
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define BLI_assert(a)
Definition BLI_assert.h:50
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
#define TIP_(msgid)
@ CD_PROP_FLOAT
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_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)
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr bool is_empty() const
Definition BLI_span.hh:510
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
int attribute_domain_size(AttrDomain domain) const
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)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
local_group_size(16, 16) .push_constant(Type b
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
void position(const bNode &, void *r_value)
static void get_closest_mesh_faces(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask &mask, const MutableSpan< int > r_face_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static void get_closest_mesh_edges(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask &mask, const MutableSpan< int > r_edge_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static void get_closest_mesh_points(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask &mask, const MutableSpan< int > r_point_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static void get_closest_mesh_corners(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask &mask, const MutableSpan< int > r_corner_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static void node_init(bNodeTree *, bNode *node)
static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
static bool component_is_available(const GeometrySet &geometry, const GeometryComponent::Type type, const AttrDomain domain)
static const GeometryComponent * find_source_component(const GeometrySet &geometry, const AttrDomain domain)
static void get_closest_mesh_tris(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask &mask, const MutableSpan< int > r_tri_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void get_closest_pointcloud_points(const PointCloud &pointcloud, const VArray< float3 > &positions, const IndexMask &mask, MutableSpan< int > r_indices, MutableSpan< float > r_distances_sq)
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 get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, const VArray< float3 > &positions, const IndexMask &mask, MutableSpan< int > r_indices, MutableSpan< float > r_distances_sq, MutableSpan< float3 > r_positions)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
const EnumPropertyItem rna_enum_attribute_domain_only_mesh_items[]
#define FLT_MAX
Definition stdcycles.h:14
BVHTree_NearestPointCallback nearest_callback
BVHTree_NearestPointCallback nearest_callback
StructRNA * srna
Definition RNA_types.hh:780
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