Blender V5.0
node_geo_raycast.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 "DNA_mesh_types.h"
6
7#include "BKE_bvhutils.hh"
8#include "BKE_mesh_sample.hh"
9
10#include "NOD_rna_define.hh"
12
14#include "UI_resources.hh"
15
16#include "RNA_enum_types.hh"
17
19
20#include "node_geometry_util.hh"
21
23
25
27
30 "INTERPOLATED",
31 0,
32 N_("Interpolated"),
33 N_("Interpolate the attribute from the corners of the hit face")},
35 "NEAREST",
36 0,
37 N_("Nearest"),
38 N_("Use the attribute value of the closest mesh element")},
39 {0, nullptr, 0, nullptr, nullptr},
40};
41
43{
44 const bNode *node = b.node_or_null();
45
46 b.add_input<decl::Geometry>("Target Geometry")
47 .only_realized_data()
48 .supported_type(GeometryComponent::Type::Mesh)
49 .description("Geometry to cast rays onto");
50 if (node != nullptr) {
51 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
52 /* TODO: Field interfacing depends on the offset of the next declarations! */
53 b.add_input(data_type, "Attribute").hide_value().field_on_all();
54 }
55 b.add_input<decl::Menu>("Interpolation")
56 .static_items(interpolation_items)
58 .description("Mapping from the target geometry to hit points");
59
60 b.add_input<decl::Vector>("Source Position")
62 .structure_type(StructureType::Dynamic);
63 b.add_input<decl::Vector>("Ray Direction")
64 .default_value({0.0f, 0.0f, -1.0f})
65 .supports_field()
66 .structure_type(StructureType::Dynamic);
67 b.add_input<decl::Float>("Ray Length")
68 .default_value(100.0f)
69 .min(0.0f)
71 .supports_field()
72 .structure_type(StructureType::Dynamic);
73
74 b.add_output<decl::Bool>("Is Hit").dependent_field({2, 3, 4});
75 b.add_output<decl::Vector>("Hit Position").dependent_field({2, 3, 4});
76 b.add_output<decl::Vector>("Hit Normal").dependent_field({2, 3, 4});
77 b.add_output<decl::Float>("Hit Distance").dependent_field({2, 3, 4});
78
79 if (node != nullptr) {
80 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
81 b.add_output(data_type, "Attribute").dependent_field({2, 3, 4});
82 }
83}
84
85static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
86{
87 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
88}
89
90static void node_init(bNodeTree * /*tree*/, bNode *node)
91{
93 data->data_type = CD_PROP_FLOAT;
94 node->storage = data;
95}
96
98{
99 const NodeDeclaration &declaration = *params.node_type().static_declaration;
102
103 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
104 eNodeSocketDatatype(params.other_socket().type));
105 if (type && *type != CD_PROP_STRING) {
106 /* The input and output sockets have the same name. */
107 params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams &params) {
108 bNode &node = params.add_node("GeometryNodeRaycast");
109 node_storage(node).data_type = *type;
110 params.update_and_connect_available_socket(node, "Attribute");
111 });
112 }
113}
114
115static void raycast_to_mesh(const IndexMask &mask,
116 const Mesh &mesh,
117 const VArray<float3> &ray_origins,
118 const VArray<float3> &ray_directions,
119 const VArray<float> &ray_lengths,
120 const MutableSpan<bool> r_hit,
121 const MutableSpan<int> r_hit_indices,
122 const MutableSpan<float3> r_hit_positions,
123 const MutableSpan<float3> r_hit_normals,
124 const MutableSpan<float> r_hit_distances)
125{
126 bke::BVHTreeFromMesh tree_data = mesh.bvh_corner_tris();
127 if (tree_data.tree == nullptr) {
128 return;
129 }
130
131 mask.foreach_index([&](const int i) {
132 const float ray_length = ray_lengths[i];
133 const float3 ray_origin = ray_origins[i];
134 const float3 ray_direction = ray_directions[i];
135
136 BVHTreeRayHit hit;
137 hit.index = -1;
138 hit.dist = ray_length;
139 if (BLI_bvhtree_ray_cast(tree_data.tree,
140 ray_origin,
141 ray_direction,
142 0.0f,
143 &hit,
144 tree_data.raycast_callback,
145 &tree_data) != -1)
146 {
147 if (!r_hit.is_empty()) {
148 r_hit[i] = hit.index >= 0;
149 }
150 if (!r_hit_indices.is_empty()) {
151 /* The caller must be able to handle invalid indices anyway, so don't clamp this value. */
152 r_hit_indices[i] = hit.index;
153 }
154 if (!r_hit_positions.is_empty()) {
155 r_hit_positions[i] = hit.co;
156 }
157 if (!r_hit_normals.is_empty()) {
158 r_hit_normals[i] = hit.no;
159 }
160 if (!r_hit_distances.is_empty()) {
161 r_hit_distances[i] = hit.dist;
162 }
163 }
164 else {
165 if (!r_hit.is_empty()) {
166 r_hit[i] = false;
167 }
168 if (!r_hit_indices.is_empty()) {
169 r_hit_indices[i] = -1;
170 }
171 if (!r_hit_positions.is_empty()) {
172 r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f);
173 }
174 if (!r_hit_normals.is_empty()) {
175 r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f);
176 }
177 if (!r_hit_distances.is_empty()) {
178 r_hit_distances[i] = ray_length;
179 }
180 }
181 });
182}
183
184class RaycastFunction : public mf::MultiFunction {
185 private:
186 GeometrySet target_;
187
188 public:
189 RaycastFunction(GeometrySet target) : target_(std::move(target))
190 {
191 target_.ensure_owns_direct_data();
192 static const mf::Signature signature = []() {
193 mf::Signature signature;
194 mf::SignatureBuilder builder{"Raycast", signature};
195 builder.single_input<float3>("Source Position");
196 builder.single_input<float3>("Ray Direction");
197 builder.single_input<float>("Ray Length");
198 builder.single_output<bool>("Is Hit", mf::ParamFlag::SupportsUnusedOutput);
199 builder.single_output<float3>("Hit Position", mf::ParamFlag::SupportsUnusedOutput);
200 builder.single_output<float3>("Hit Normal", mf::ParamFlag::SupportsUnusedOutput);
201 builder.single_output<float>("Distance", mf::ParamFlag::SupportsUnusedOutput);
202 builder.single_output<int>("Triangle Index", mf::ParamFlag::SupportsUnusedOutput);
203 return signature;
204 }();
205 this->set_signature(&signature);
206 }
207
208 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
209 {
210 BLI_assert(target_.has_mesh());
211 const Mesh &mesh = *target_.get_mesh();
212
214 mesh,
215 params.readonly_single_input<float3>(0, "Source Position"),
216 params.readonly_single_input<float3>(1, "Ray Direction"),
217 params.readonly_single_input<float>(2, "Ray Length"),
218 params.uninitialized_single_output_if_required<bool>(3, "Is Hit"),
219 params.uninitialized_single_output_if_required<int>(7, "Triangle Index"),
220 params.uninitialized_single_output_if_required<float3>(4, "Hit Position"),
221 params.uninitialized_single_output_if_required<float3>(5, "Hit Normal"),
222 params.uninitialized_single_output_if_required<float>(6, "Distance"));
223 }
224};
225
227{
228 GeometrySet target = params.extract_input<GeometrySet>("Target Geometry");
229 const auto mapping = params.get_input<GeometryNodeRaycastMapMode>("Interpolation");
230
231 if (target.is_empty()) {
232 params.set_default_remaining_outputs();
233 return;
234 }
235
236 if (!target.has_mesh()) {
237 params.set_default_remaining_outputs();
238 return;
239 }
240
241 if (target.get_mesh()->faces_num == 0) {
242 params.error_message_add(NodeWarningType::Error, TIP_("The target mesh must have faces"));
243 params.set_default_remaining_outputs();
244 return;
245 }
246
247 std::string error_message;
248
249 bke::SocketValueVariant normalized_direction;
250 {
251 auto ray_direction = params.extract_input<bke::SocketValueVariant>("Ray Direction");
252
253 static auto normalize_fn = mf::build::SI1_SO<float3, float3>(
254 "Normalize",
255 [](const float3 &v) { return math::normalize(v); },
256 mf::build::exec_presets::AllSpanOrSingle());
257
259 {&ray_direction},
260 {&normalized_direction},
261 params.user_data(),
262 error_message))
263 {
264 params.set_default_remaining_outputs();
265 params.error_message_add(NodeWarningType::Error, std::move(error_message));
266 return;
267 }
268 }
269
270 auto position = params.extract_input<bke::SocketValueVariant>("Source Position");
271 auto ray_length = params.extract_input<bke::SocketValueVariant>("Ray Length");
272
274 bke::SocketValueVariant hit_position;
275 bke::SocketValueVariant hit_normal;
276 bke::SocketValueVariant hit_distance;
277 bke::SocketValueVariant triangle_index;
279 std::make_unique<RaycastFunction>(target),
280 {&position, &normalized_direction, &ray_length},
281 {&is_hit, &hit_position, &hit_normal, &hit_distance, &triangle_index},
282 params.user_data(),
283 error_message))
284 {
285 params.set_default_remaining_outputs();
286 params.error_message_add(NodeWarningType::Error, std::move(error_message));
287 return;
288 }
289
290 params.set_output("Is Hit", std::move(is_hit));
291 params.set_output("Hit Position", hit_position);
292 params.set_output("Hit Normal", std::move(hit_normal));
293 params.set_output("Hit Distance", std::move(hit_distance));
294
295 if (!params.output_is_required("Attribute")) {
296 return;
297 }
298
299 GField field = params.extract_input<GField>("Attribute");
300 bke::SocketValueVariant bary_weights;
301 bke::SocketValueVariant triangle_index_copy = triangle_index;
302 switch (mapping) {
305 std::make_shared<bke::mesh_surface_sample::BaryWeightFromPositionFn>(target),
306 {&hit_position, &triangle_index_copy},
307 {&bary_weights},
308 params.user_data(),
309 error_message))
310 {
311 params.set_default_remaining_outputs();
312 params.error_message_add(NodeWarningType::Error, std::move(error_message));
313 return;
314 }
315 break;
318 std::make_shared<bke::mesh_surface_sample::CornerBaryWeightFromPositionFn>(target),
319 {&hit_position, &triangle_index_copy},
320 {&bary_weights},
321 params.user_data(),
322 error_message))
323 {
324 params.set_default_remaining_outputs();
325 params.error_message_add(NodeWarningType::Error, std::move(error_message));
326 return;
327 }
328 break;
329 }
330
331 bke::SocketValueVariant sampled_atribute;
333 std::make_shared<bke::mesh_surface_sample::BaryWeightSampleFn>(std::move(target),
334 std::move(field)),
335 {&triangle_index, &bary_weights},
336 {&sampled_atribute},
337 params.user_data(),
338 error_message))
339 {
340 params.set_default_remaining_outputs();
341 params.error_message_add(NodeWarningType::Error, std::move(error_message));
342 return;
343 }
344
345 params.set_output("Attribute", std::move(sampled_atribute));
346}
347
348static void node_rna(StructRNA *srna)
349{
351 "data_type",
352 "Data Type",
353 "Type of data stored in attribute",
358}
359
360static void node_register()
361{
362 static blender::bke::bNodeType ntype;
363
364 geo_node_type_base(&ntype, "GeometryNodeRaycast", GEO_NODE_RAYCAST);
365 ntype.ui_name = "Raycast";
366 ntype.ui_description =
367 "Cast rays from the context geometry onto a target geometry, and retrieve information from "
368 "each hit point";
369 ntype.enum_name_legacy = "RAYCAST";
372 ntype.initfunc = node_init;
374 ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage);
375 ntype.declare = node_declare;
380
381 node_rna(ntype.rna_ext.srna);
382}
384
385} // namespace blender::nodes::node_geo_raycast_cc
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_RAYCAST
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
#define TIP_(msgid)
#define IFACE_(msgid)
@ CD_PROP_FLOAT
@ CD_PROP_STRING
@ NODE_DEFAULT_INPUT_POSITION_FIELD
eNodeSocketDatatype
GeometryNodeRaycastMapMode
@ GEO_NODE_RAYCAST_NEAREST
@ GEO_NODE_RAYCAST_INTERPOLATED
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
@ PROP_DISTANCE
Definition RNA_types.hh:256
#define UI_ITEM_NONE
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
constexpr bool is_empty() const
Definition BLI_span.hh:509
void set_signature(const Signature *signature)
Vector< SocketDeclaration * > inputs
Vector< SocketDeclaration * > outputs
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
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
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
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
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
const EnumPropertyItem * attribute_type_type_with_socket_fn(bContext *, PointerRNA *, PropertyRNA *, bool *r_free)
static EnumPropertyItem interpolation_items[]
static void node_geo_exec(GeoNodeExecParams params)
static void node_init(bNodeTree *, bNode *node)
static void node_rna(StructRNA *srna)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void raycast_to_mesh(const IndexMask &mask, const Mesh &mesh, const VArray< float3 > &ray_origins, const VArray< float3 > &ray_directions, const VArray< float > &ray_lengths, const MutableSpan< bool > r_hit, const MutableSpan< int > r_hit_indices, const MutableSpan< float3 > r_hit_positions, const MutableSpan< float3 > r_hit_normals, const MutableSpan< float > r_hit_distances)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void node_declare(NodeDeclarationBuilder &b)
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)
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
const EnumPropertyItem rna_enum_attribute_type_items[]
StructRNA * srna
int faces_num
void * storage
BVHTree_RayCastCallback raycast_callback
const Mesh * get_mesh() const
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
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238