Blender V5.0
node_geo_sample_nearest_surface.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 "BKE_bvhutils.hh"
6#include "BKE_mesh.hh"
7#include "BKE_mesh_sample.hh"
8
9#include "NOD_rna_define.hh"
11
13#include "UI_resources.hh"
14
15#include "RNA_enum_types.hh"
16
17#include "BLI_task.hh"
18
19#include "node_geometry_util.hh"
20
22
24
26{
27 const bNode *node = b.node_or_null();
28
29 b.add_input<decl::Geometry>("Mesh")
30 .supported_type(GeometryComponent::Type::Mesh)
31 .description("Mesh to find the closest surface point on");
32 if (node != nullptr) {
33 const eCustomDataType data_type = eCustomDataType(node->custom1);
34 b.add_input(data_type, "Value").hide_value().field_on_all();
35 }
36 b.add_input<decl::Int>("Group ID")
37 .hide_value()
38 .field_on_all()
40 "Splits the faces of the input mesh into groups which can be sampled individually");
41 b.add_input<decl::Vector>("Sample Position")
43 .structure_type(StructureType::Dynamic);
44 b.add_input<decl::Int>("Sample Group ID")
45 .hide_value()
46 .supports_field()
47 .structure_type(StructureType::Dynamic);
48
49 if (node != nullptr) {
50 const eCustomDataType data_type = eCustomDataType(node->custom1);
51 b.add_output(data_type, "Value").dependent_field({3, 4});
52 }
53 b.add_output<decl::Bool>("Is Valid")
54 .dependent_field({3, 4})
55 .description(
56 "Whether the sampling was successful. It can fail when the sampled group is empty");
57}
58
59static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
60{
61 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
62}
63
64static void node_init(bNodeTree * /*tree*/, bNode *node)
65{
66 node->custom1 = CD_PROP_FLOAT;
67}
68
70{
71 const NodeDeclaration &declaration = *params.node_type().static_declaration;
73
74 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
75 eNodeSocketDatatype(params.other_socket().type));
76 if (type && *type != CD_PROP_STRING) {
77 /* The input and output sockets have the same name. */
78 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
79 bNode &node = params.add_node("GeometryNodeSampleNearestSurface");
80 node.custom1 = *type;
81 params.update_and_connect_available_socket(node, "Value");
82 });
83 }
84}
85
86class SampleNearestSurfaceFunction : public mf::MultiFunction {
87 private:
88 GeometrySet source_;
90 VectorSet<int> group_indices_;
91
92 public:
94 : source_(std::move(geometry))
95 {
96 source_.ensure_owns_direct_data();
97 static const mf::Signature signature = []() {
98 mf::Signature signature;
99 mf::SignatureBuilder builder{"Sample Nearest Surface", signature};
100 builder.single_input<float3>("Position");
101 builder.single_input<int>("Sample ID");
102 builder.single_output<int>("Triangle Index");
103 builder.single_output<float3>("Sample Position");
104 builder.single_output<bool>("Is Valid", mf::ParamFlag::SupportsUnusedOutput);
105 return signature;
106 }();
107 this->set_signature(&signature);
108
109 const Mesh &mesh = *source_.get_mesh();
110
111 /* Compute group ids on mesh. */
113 FieldEvaluator field_evaluator{field_context, mesh.faces_num};
114 field_evaluator.add(group_id_field);
115 field_evaluator.evaluate();
116 const VArray<int> group_ids = field_evaluator.get_evaluated<int>(0);
117
118 /* Compute index masks for groups. */
119 IndexMaskMemory memory;
121 group_ids, memory, group_indices_);
122 const int groups_num = group_masks.size();
123
124 /* Construct BVH tree for each group. */
125 bvh_trees_.reinitialize(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 bvh_trees_[group_i] = bke::bvhtree_from_mesh_tris_init(mesh, group_mask);
133 }
134 },
136 [&](const int group_i) { return group_masks[group_i].size(); }, mesh.faces_num));
137 }
138
139 ~SampleNearestSurfaceFunction() override = default;
140
141 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
142 {
143 const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position");
144 const VArray<int> &sample_ids = params.readonly_single_input<int>(1, "Sample ID");
145 MutableSpan<int> triangle_index = params.uninitialized_single_output<int>(2, "Triangle Index");
146 MutableSpan<float3> sample_position = params.uninitialized_single_output<float3>(
147 3, "Sample Position");
148 MutableSpan<bool> is_valid_span = params.uninitialized_single_output_if_required<bool>(
149 4, "Is Valid");
150
151 mask.foreach_index([&](const int i) {
152 const float3 position = positions[i];
153 const int sample_id = sample_ids[i];
154 const int group_index = group_indices_.index_of_try(sample_id);
155 if (group_index == -1) {
156 triangle_index[i] = -1;
157 sample_position[i] = float3(0, 0, 0);
158 if (!is_valid_span.is_empty()) {
159 is_valid_span[i] = false;
160 }
161 return;
162 }
163 const bke::BVHTreeFromMesh &bvh = bvh_trees_[group_index];
164 BVHTreeNearest nearest;
165 nearest.dist_sq = FLT_MAX;
166 nearest.index = -1;
168 position,
169 &nearest,
171 const_cast<bke::BVHTreeFromMesh *>(&bvh));
172 triangle_index[i] = nearest.index;
173 sample_position[i] = nearest.co;
174 if (!is_valid_span.is_empty()) {
175 is_valid_span[i] = true;
176 }
177 });
178 }
179
181 {
182 ExecutionHints hints;
183 hints.min_grain_size = 512;
184 return hints;
185 }
186};
187
189{
190 GeometrySet geometry = params.extract_input<GeometrySet>("Mesh");
191 const Mesh *mesh = geometry.get_mesh();
192 if (mesh == nullptr) {
193 params.set_default_remaining_outputs();
194 return;
195 }
196 if (mesh->verts_num == 0) {
197 params.set_default_remaining_outputs();
198 return;
199 }
200 if (mesh->faces_num == 0) {
201 params.error_message_add(NodeWarningType::Error, TIP_("The source mesh must have faces"));
202 params.set_default_remaining_outputs();
203 return;
204 }
205
206 GField value = params.extract_input<GField>("Value");
207 Field<int> group_id_field = params.extract_input<Field<int>>("Group ID");
208 auto sample_position = params.extract_input<bke::SocketValueVariant>("Sample Position");
209 auto sample_group_id = params.extract_input<bke::SocketValueVariant>("Sample Group ID");
210
211 std::string error_message;
212
213 bke::SocketValueVariant triangle_index;
214 bke::SocketValueVariant nearest_positions;
217 std::make_shared<SampleNearestSurfaceFunction>(geometry, group_id_field),
218 {&sample_position, &sample_group_id},
219 {&triangle_index, &nearest_positions, &is_valid},
220 params.user_data(),
221 error_message))
222 {
223 params.set_default_remaining_outputs();
224 params.error_message_add(NodeWarningType::Error, std::move(error_message));
225 return;
226 }
227
228 bke::SocketValueVariant bary_weights;
229 bke::SocketValueVariant triangle_index_copy = triangle_index;
231 std::make_shared<bke::mesh_surface_sample::BaryWeightFromPositionFn>(geometry),
232 {&nearest_positions, &triangle_index_copy},
233 {&bary_weights},
234 params.user_data(),
235 error_message))
236 {
237 params.set_default_remaining_outputs();
238 params.error_message_add(NodeWarningType::Error, std::move(error_message));
239 return;
240 }
241
242 bke::SocketValueVariant sample_value;
244 std::make_shared<bke::mesh_surface_sample::BaryWeightSampleFn>(geometry,
245 std::move(value)),
246 {&triangle_index, &bary_weights},
247 {&sample_value},
248 params.user_data(),
249 error_message))
250 {
251 params.set_default_remaining_outputs();
252 params.error_message_add(NodeWarningType::Error, std::move(error_message));
253 return;
254 }
255
256 params.set_output("Value", std::move(sample_value));
257 params.set_output("Is Valid", std::move(is_valid));
258}
259
260static void node_rna(StructRNA *srna)
261{
263 "data_type",
264 "Data Type",
265 "",
270}
271
272static void node_register()
273{
274 static blender::bke::bNodeType ntype;
275
276 geo_node_type_base(&ntype, "GeometryNodeSampleNearestSurface", GEO_NODE_SAMPLE_NEAREST_SURFACE);
277 ntype.ui_name = "Sample Nearest Surface";
278 ntype.ui_description =
279 "Calculate the interpolated value of a mesh attribute on the closest point of its surface";
280 ntype.enum_name_legacy = "SAMPLE_NEAREST_SURFACE";
282 ntype.initfunc = node_init;
283 ntype.declare = node_declare;
289
290 node_rna(ntype.rna_ext.srna);
291}
293
294} // namespace blender::nodes::node_geo_sample_nearest_surface_cc
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_SAMPLE_NEAREST_SURFACE
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
#define TIP_(msgid)
#define IFACE_(msgid)
@ CD_PROP_FLOAT
@ CD_PROP_STRING
@ NODE_DEFAULT_INPUT_POSITION_FIELD
eNodeSocketDatatype
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
#define UI_ITEM_NONE
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)
Vector< SocketDeclaration * > inputs
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
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_tris_init(const Mesh &mesh, const IndexMask &faces_mask)
Definition bvhutils.cc:784
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:5144
void node_type_size_preset(bNodeType &ntype, eNodeSizePreset size)
Definition node.cc:5396
const EnumPropertyItem * attribute_type_type_with_socket_fn(bContext *, PointerRNA *, PropertyRNA *, bool *r_free)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params, Span< SocketDeclaration * > declarations)
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)
const EnumPropertyItem rna_enum_attribute_type_items[]
#define FLT_MAX
Definition stdcycles.h:14
StructRNA * srna
int16_t custom1
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
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:378
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