Blender V4.5
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
13#include "UI_interface.hh"
14#include "UI_resources.hh"
15
16#include "RNA_enum_types.hh"
17
19
20#include "node_geometry_util.hh"
21
23
25
27
29{
30 const bNode *node = b.node_or_null();
31
32 b.add_input<decl::Geometry>("Target Geometry")
34 .supported_type(GeometryComponent::Type::Mesh);
35 if (node != nullptr) {
36 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
37 /* TODO: Field interfacing depends on the offset of the next declarations! */
38 b.add_input(data_type, "Attribute").hide_value().field_on_all();
39 }
40
41 b.add_input<decl::Vector>("Source Position").implicit_field(NODE_DEFAULT_INPUT_POSITION_FIELD);
42 b.add_input<decl::Vector>("Ray Direction").default_value({0.0f, 0.0f, -1.0f}).supports_field();
43 b.add_input<decl::Float>("Ray Length")
44 .default_value(100.0f)
45 .min(0.0f)
46 .subtype(PROP_DISTANCE)
47 .supports_field();
48
49 b.add_output<decl::Bool>("Is Hit").dependent_field({2, 3, 4});
50 b.add_output<decl::Vector>("Hit Position").dependent_field({2, 3, 4});
51 b.add_output<decl::Vector>("Hit Normal").dependent_field({2, 3, 4});
52 b.add_output<decl::Float>("Hit Distance").dependent_field({2, 3, 4});
53
54 if (node != nullptr) {
55 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
56 b.add_output(data_type, "Attribute").dependent_field({2, 3, 4});
57 }
58}
59
60static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
61{
62 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
63 layout->prop(ptr, "mapping", UI_ITEM_NONE, "", ICON_NONE);
64}
65
66static void node_init(bNodeTree * /*tree*/, bNode *node)
67{
70 data->data_type = CD_PROP_FLOAT;
71 node->storage = data;
72}
73
75{
76 const NodeDeclaration &declaration = *params.node_type().static_declaration;
79
80 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
81 eNodeSocketDatatype(params.other_socket().type));
82 if (type && *type != CD_PROP_STRING) {
83 /* The input and output sockets have the same name. */
84 params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams &params) {
85 bNode &node = params.add_node("GeometryNodeRaycast");
86 node_storage(node).data_type = *type;
87 params.update_and_connect_available_socket(node, "Attribute");
88 });
89 }
90}
91
92static void raycast_to_mesh(const IndexMask &mask,
93 const Mesh &mesh,
94 const VArray<float3> &ray_origins,
95 const VArray<float3> &ray_directions,
96 const VArray<float> &ray_lengths,
97 const MutableSpan<bool> r_hit,
98 const MutableSpan<int> r_hit_indices,
99 const MutableSpan<float3> r_hit_positions,
100 const MutableSpan<float3> r_hit_normals,
101 const MutableSpan<float> r_hit_distances)
102{
103 bke::BVHTreeFromMesh tree_data = mesh.bvh_corner_tris();
104 if (tree_data.tree == nullptr) {
105 return;
106 }
107
108 mask.foreach_index([&](const int i) {
109 const float ray_length = ray_lengths[i];
110 const float3 ray_origin = ray_origins[i];
111 const float3 ray_direction = ray_directions[i];
112
113 BVHTreeRayHit hit;
114 hit.index = -1;
115 hit.dist = ray_length;
116 if (BLI_bvhtree_ray_cast(tree_data.tree,
117 ray_origin,
118 ray_direction,
119 0.0f,
120 &hit,
121 tree_data.raycast_callback,
122 &tree_data) != -1)
123 {
124 if (!r_hit.is_empty()) {
125 r_hit[i] = hit.index >= 0;
126 }
127 if (!r_hit_indices.is_empty()) {
128 /* The caller must be able to handle invalid indices anyway, so don't clamp this value. */
129 r_hit_indices[i] = hit.index;
130 }
131 if (!r_hit_positions.is_empty()) {
132 r_hit_positions[i] = hit.co;
133 }
134 if (!r_hit_normals.is_empty()) {
135 r_hit_normals[i] = hit.no;
136 }
137 if (!r_hit_distances.is_empty()) {
138 r_hit_distances[i] = hit.dist;
139 }
140 }
141 else {
142 if (!r_hit.is_empty()) {
143 r_hit[i] = false;
144 }
145 if (!r_hit_indices.is_empty()) {
146 r_hit_indices[i] = -1;
147 }
148 if (!r_hit_positions.is_empty()) {
149 r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f);
150 }
151 if (!r_hit_normals.is_empty()) {
152 r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f);
153 }
154 if (!r_hit_distances.is_empty()) {
155 r_hit_distances[i] = ray_length;
156 }
157 }
158 });
159}
160
161class RaycastFunction : public mf::MultiFunction {
162 private:
163 GeometrySet target_;
164
165 public:
166 RaycastFunction(GeometrySet target) : target_(std::move(target))
167 {
168 target_.ensure_owns_direct_data();
169 static const mf::Signature signature = []() {
170 mf::Signature signature;
171 mf::SignatureBuilder builder{"Raycast", signature};
172 builder.single_input<float3>("Source Position");
173 builder.single_input<float3>("Ray Direction");
174 builder.single_input<float>("Ray Length");
175 builder.single_output<bool>("Is Hit", mf::ParamFlag::SupportsUnusedOutput);
176 builder.single_output<float3>("Hit Position", mf::ParamFlag::SupportsUnusedOutput);
177 builder.single_output<float3>("Hit Normal", mf::ParamFlag::SupportsUnusedOutput);
178 builder.single_output<float>("Distance", mf::ParamFlag::SupportsUnusedOutput);
179 builder.single_output<int>("Triangle Index", mf::ParamFlag::SupportsUnusedOutput);
180 return signature;
181 }();
182 this->set_signature(&signature);
183 }
184
185 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
186 {
187 BLI_assert(target_.has_mesh());
188 const Mesh &mesh = *target_.get_mesh();
189
191 mesh,
192 params.readonly_single_input<float3>(0, "Source Position"),
193 params.readonly_single_input<float3>(1, "Ray Direction"),
194 params.readonly_single_input<float>(2, "Ray Length"),
195 params.uninitialized_single_output_if_required<bool>(3, "Is Hit"),
196 params.uninitialized_single_output_if_required<int>(7, "Triangle Index"),
197 params.uninitialized_single_output_if_required<float3>(4, "Hit Position"),
198 params.uninitialized_single_output_if_required<float3>(5, "Hit Normal"),
199 params.uninitialized_single_output_if_required<float>(6, "Distance"));
200 }
201};
202
204{
205 GeometrySet target = params.extract_input<GeometrySet>("Target Geometry");
206 const NodeGeometryRaycast &storage = node_storage(params.node());
207 const GeometryNodeRaycastMapMode mapping = GeometryNodeRaycastMapMode(storage.mapping);
208
209 if (target.is_empty()) {
210 params.set_default_remaining_outputs();
211 return;
212 }
213
214 if (!target.has_mesh()) {
215 params.set_default_remaining_outputs();
216 return;
217 }
218
219 if (target.get_mesh()->faces_num == 0) {
220 params.error_message_add(NodeWarningType::Error, TIP_("The target mesh must have faces"));
221 params.set_default_remaining_outputs();
222 return;
223 }
224
225 static auto normalize_fn = mf::build::SI1_SO<float3, float3>(
226 "Normalize",
227 [](const float3 &v) { return math::normalize(v); },
228 mf::build::exec_presets::AllSpanOrSingle());
229 auto direction_op = FieldOperation::Create(
230 normalize_fn, {params.extract_input<Field<float3>>("Ray Direction")});
231
232 auto op = FieldOperation::Create(std::make_unique<RaycastFunction>(target),
233 {params.extract_input<Field<float3>>("Source Position"),
234 Field<float3>(direction_op),
235 params.extract_input<Field<float>>("Ray Length")});
236
237 Field<float3> hit_position(op, 1);
238 params.set_output("Is Hit", Field<bool>(op, 0));
239 params.set_output("Hit Position", hit_position);
240 params.set_output("Hit Normal", Field<float3>(op, 2));
241 params.set_output("Hit Distance", Field<float>(op, 3));
242
243 if (!params.output_is_required("Attribute")) {
244 return;
245 }
246
247 GField field = params.extract_input<GField>("Attribute");
248 Field<int> triangle_index(op, 4);
249 Field<float3> bary_weights;
250 switch (mapping) {
253 std::make_shared<bke::mesh_surface_sample::BaryWeightFromPositionFn>(target),
254 {hit_position, triangle_index}));
255 break;
258 std::make_shared<bke::mesh_surface_sample::CornerBaryWeightFromPositionFn>(target),
259 {hit_position, triangle_index}));
260 break;
261 }
262 auto sample_op = FieldOperation::Create(
263 std::make_shared<bke::mesh_surface_sample::BaryWeightSampleFn>(std::move(target),
264 std::move(field)),
265 {triangle_index, bary_weights});
266 params.set_output("Attribute", GField(sample_op));
267}
268
269static void node_rna(StructRNA *srna)
270{
271 static EnumPropertyItem mapping_items[] = {
273 "INTERPOLATED",
274 0,
275 "Interpolated",
276 "Interpolate the attribute from the corners of the hit face"},
278 "NEAREST",
279 0,
280 "Nearest",
281 "Use the attribute value of the closest mesh element"},
282 {0, nullptr, 0, nullptr, nullptr},
283 };
284
286 "mapping",
287 "Mapping",
288 "Mapping from the target geometry to hit points",
289 mapping_items,
293 "data_type",
294 "Data Type",
295 "Type of data stored in attribute",
300}
301
302static void node_register()
303{
304 static blender::bke::bNodeType ntype;
305
306 geo_node_type_base(&ntype, "GeometryNodeRaycast", GEO_NODE_RAYCAST);
307 ntype.ui_name = "Raycast";
308 ntype.ui_description =
309 "Cast rays from the context geometry onto a target geometry, and retrieve information from "
310 "each hit point";
311 ntype.enum_name_legacy = "RAYCAST";
314 ntype.initfunc = node_init;
316 ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage);
317 ntype.declare = node_declare;
322
323 node_rna(ntype.rna_ext.srna);
324}
326
327} // namespace blender::nodes::node_geo_raycast_cc
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:447
#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:244
#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)
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
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:2748
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:5603
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:5355
void node_type_size_preset(bNodeType &ntype, eNodeSizePreset size)
Definition node.cc:5585
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 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)
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[]
#define min(a, b)
Definition sort.cc:36
StructRNA * srna
Definition RNA_types.hh:909
int faces_num
void * storage
BVHTree_RayCastCallback raycast_callback
const Mesh * get_mesh() const
Defines a node type.
Definition BKE_node.hh:226
std::string ui_description
Definition BKE_node.hh:232
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:347
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:371
NodeDeclareFunction declare
Definition BKE_node.hh:355
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:4226