Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-10-19 17:01:39 +0300
committerHans Goudey <h.goudey@me.com>2021-10-19 17:01:39 +0300
commit0a6cf3ed0c64a0e4e58ecd40a491d0e6c93532f2 (patch)
tree4e2ba0b74c0ceaa0956aa944ec0b473cf2926cd4 /source
parent9a1d75e0b9580819dba188afdab72295cbf47302 (diff)
Geometry Nodes: Fields version of the raycast node
This patch includes an updated version of the raycast node that uses fields instead of attributes for inputs instead of outputs. This makes the node's UI much clearer. It should be faster too, since the evaluation system for fields provides multi-threading. The source position replaces the input geometry (since this node is evaluated in the context of a geometry like the other field nodes). Thanks to @guitargeek for an initial version of this patch. Differential Revision: https://developer.blender.org/D12638
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_mesh_sample.hh3
-rw-r--r--source/blender/blenkernel/BKE_node.h1
-rw-r--r--source/blender/blenkernel/intern/mesh_sample.cc33
-rw-r--r--source/blender/blenkernel/intern/node.cc2
-rw-r--r--source/blender/makesdna/DNA_node_types.h6
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c35
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_geometry.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h3
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc456
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc4
13 files changed, 522 insertions, 31 deletions
diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh
index 4845876751b..17f8e766724 100644
--- a/source/blender/blenkernel/BKE_mesh_sample.hh
+++ b/source/blender/blenkernel/BKE_mesh_sample.hh
@@ -75,6 +75,7 @@ enum class eAttributeMapMode {
class MeshAttributeInterpolator {
private:
const Mesh *mesh_;
+ const IndexMask mask_;
const Span<float3> positions_;
const Span<int> looptri_indices_;
@@ -83,13 +84,13 @@ class MeshAttributeInterpolator {
public:
MeshAttributeInterpolator(const Mesh *mesh,
+ const IndexMask mask,
const Span<float3> positions,
const Span<int> looptri_indices);
void sample_data(const GVArray &src,
const AttributeDomain domain,
const eAttributeMapMode mode,
- const IndexMask mask,
const GMutableSpan dst);
void sample_attribute(const ReadAttributeLookup &src_attribute,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 65e54be7db4..07ad317dd30 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1544,6 +1544,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_TRANSFER_ATTRIBUTE 1125
#define GEO_NODE_SUBDIVISION_SURFACE 1126
#define GEO_NODE_CURVE_ENDPOINT_SELECTION 1127
+#define GEO_NODE_RAYCAST 1128
/** \} */
diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc
index 9842b3aff31..2274d34f0f1 100644
--- a/source/blender/blenkernel/intern/mesh_sample.cc
+++ b/source/blender/blenkernel/intern/mesh_sample.cc
@@ -60,8 +60,6 @@ void sample_point_attribute(const Mesh &mesh,
const IndexMask mask,
const GMutableSpan data_out)
{
- BLI_assert(data_out.size() == looptri_indices.size());
- BLI_assert(data_out.size() == bary_coords.size());
BLI_assert(data_in.size() == mesh.totvert);
BLI_assert(data_in.type() == data_out.type());
@@ -109,8 +107,6 @@ void sample_corner_attribute(const Mesh &mesh,
const IndexMask mask,
const GMutableSpan data_out)
{
- BLI_assert(data_out.size() == looptri_indices.size());
- BLI_assert(data_out.size() == bary_coords.size());
BLI_assert(data_in.size() == mesh.totloop);
BLI_assert(data_in.type() == data_out.type());
@@ -146,7 +142,6 @@ void sample_face_attribute(const Mesh &mesh,
const IndexMask mask,
const GMutableSpan data_out)
{
- BLI_assert(data_out.size() == looptri_indices.size());
BLI_assert(data_in.size() == mesh.totpoly);
BLI_assert(data_in.type() == data_out.type());
@@ -158,9 +153,10 @@ void sample_face_attribute(const Mesh &mesh,
}
MeshAttributeInterpolator::MeshAttributeInterpolator(const Mesh *mesh,
+ const IndexMask mask,
const Span<float3> positions,
const Span<int> looptri_indices)
- : mesh_(mesh), positions_(positions), looptri_indices_(looptri_indices)
+ : mesh_(mesh), mask_(mask), positions_(positions), looptri_indices_(looptri_indices)
{
BLI_assert(positions.size() == looptri_indices.size());
}
@@ -168,15 +164,15 @@ MeshAttributeInterpolator::MeshAttributeInterpolator(const Mesh *mesh,
Span<float3> MeshAttributeInterpolator::ensure_barycentric_coords()
{
if (!bary_coords_.is_empty()) {
- BLI_assert(bary_coords_.size() == positions_.size());
+ BLI_assert(bary_coords_.size() >= mask_.min_array_size());
return bary_coords_;
}
- bary_coords_.reinitialize(positions_.size());
+ bary_coords_.reinitialize(mask_.min_array_size());
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh_),
BKE_mesh_runtime_looptri_len(mesh_)};
- for (const int i : bary_coords_.index_range()) {
+ for (const int i : mask_) {
const int looptri_index = looptri_indices_[i];
const MLoopTri &looptri = looptris[looptri_index];
@@ -196,15 +192,15 @@ Span<float3> MeshAttributeInterpolator::ensure_barycentric_coords()
Span<float3> MeshAttributeInterpolator::ensure_nearest_weights()
{
if (!nearest_weights_.is_empty()) {
- BLI_assert(nearest_weights_.size() == positions_.size());
+ BLI_assert(nearest_weights_.size() >= mask_.min_array_size());
return nearest_weights_;
}
- nearest_weights_.reinitialize(positions_.size());
+ nearest_weights_.reinitialize(mask_.min_array_size());
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh_),
BKE_mesh_runtime_looptri_len(mesh_)};
- for (const int i : nearest_weights_.index_range()) {
+ for (const int i : mask_) {
const int looptri_index = looptri_indices_[i];
const MLoopTri &looptri = looptris[looptri_index];
@@ -224,7 +220,6 @@ Span<float3> MeshAttributeInterpolator::ensure_nearest_weights()
void MeshAttributeInterpolator::sample_data(const GVArray &src,
const AttributeDomain domain,
const eAttributeMapMode mode,
- const IndexMask mask,
const GMutableSpan dst)
{
if (src.is_empty() || dst.is_empty()) {
@@ -247,15 +242,15 @@ void MeshAttributeInterpolator::sample_data(const GVArray &src,
/* Interpolate the source attributes on the surface. */
switch (domain) {
case ATTR_DOMAIN_POINT: {
- sample_point_attribute(*mesh_, looptri_indices_, weights, src, mask, dst);
+ sample_point_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst);
break;
}
case ATTR_DOMAIN_FACE: {
- sample_face_attribute(*mesh_, looptri_indices_, src, mask, dst);
+ sample_face_attribute(*mesh_, looptri_indices_, src, mask_, dst);
break;
}
case ATTR_DOMAIN_CORNER: {
- sample_corner_attribute(*mesh_, looptri_indices_, weights, src, mask, dst);
+ sample_corner_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst);
break;
}
case ATTR_DOMAIN_EDGE: {
@@ -274,11 +269,7 @@ void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_
eAttributeMapMode mode)
{
if (src_attribute && dst_attribute) {
- this->sample_data(*src_attribute.varray,
- src_attribute.domain,
- mode,
- IndexMask(dst_attribute->size()),
- dst_attribute.as_span());
+ this->sample_data(*src_attribute.varray, src_attribute.domain, mode, dst_attribute.as_span());
}
}
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index c5fb9030847..ce8f941b0b6 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -5751,6 +5751,7 @@ static void registerGeometryNodes()
register_node_type_geo_legacy_curve_subdivide();
register_node_type_geo_legacy_edge_split();
register_node_type_geo_legacy_subdivision_surface();
+ register_node_type_geo_legacy_raycast();
register_node_type_geo_align_rotation_to_vector();
register_node_type_geo_attribute_capture();
@@ -5814,7 +5815,6 @@ static void registerGeometryNodes()
register_node_type_geo_instance_on_points();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
- register_node_type_geo_material_replace();
register_node_type_geo_material_selection();
register_node_type_geo_mesh_primitive_circle();
register_node_type_geo_mesh_primitive_cone();
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index dd749a9dc60..e6d02923dfe 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1526,9 +1526,13 @@ typedef struct NodeGeometryRaycast {
/* GeometryNodeRaycastMapMode. */
uint8_t mapping;
+ /* CustomDataType. */
+ int8_t data_type;
+
+ /* Deprecated input types in new Raycast node. Can be removed when legacy nodes are no longer
+ * supported. */
uint8_t input_type_ray_direction;
uint8_t input_type_ray_length;
- char _pad[1];
} NodeGeometryRaycast;
typedef struct NodeGeometryCurveFill {
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 9571d889f6d..dfefc774b3d 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -10716,7 +10716,7 @@ static void def_geo_input_material(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
-static void def_geo_raycast(StructRNA *srna)
+static void def_geo_legacy_raycast(StructRNA *srna)
{
static EnumPropertyItem mapping_items[] = {
{GEO_NODE_RAYCAST_INTERPOLATED,
@@ -10752,6 +10752,39 @@ static void def_geo_raycast(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_raycast(StructRNA *srna)
+{
+ static EnumPropertyItem mapping_items[] = {
+ {GEO_NODE_RAYCAST_INTERPOLATED,
+ "INTERPOLATED",
+ 0,
+ "Interpolated",
+ "Interpolate the attribute from the corners of the hit face"},
+ {GEO_NODE_RAYCAST_NEAREST,
+ "NEAREST",
+ 0,
+ "Nearest",
+ "Use the attribute value of the closest mesh element"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryRaycast", "storage");
+
+ prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mapping_items);
+ RNA_def_property_ui_text(prop, "Mapping", "Mapping from the target geometry to hit points");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
+ RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+}
+
static void def_geo_curve_fill(StructRNA *srna)
{
static const EnumPropertyItem mode_items[] = {
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 35cd1c8c5a9..4d006342e72 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -255,6 +255,7 @@ set(SRC
geometry/nodes/node_geo_points_to_vertices.cc
geometry/nodes/node_geo_points_to_volume.cc
geometry/nodes/node_geo_proximity.cc
+ geometry/nodes/node_geo_raycast.cc
geometry/nodes/node_geo_realize_instances.cc
geometry/nodes/node_geo_rotate_instances.cc
geometry/nodes/node_geo_scale_instances.cc
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 3ca8c0d02d7..4d75303363c 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -45,6 +45,7 @@ void register_node_type_geo_legacy_select_by_handle_type(void);
void register_node_type_geo_legacy_curve_subdivide(void);
void register_node_type_geo_legacy_edge_split(void);
void register_node_type_geo_legacy_subdivision_surface(void);
+void register_node_type_geo_legacy_raycast(void);
void register_node_type_geo_align_rotation_to_vector(void);
void register_node_type_geo_attribute_capture(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 9a361c3a3f1..c20a9545968 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -315,7 +315,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_
DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "")
-DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "")
@@ -383,6 +383,7 @@ DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO",
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "")
+DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "")
DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "")
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
index 6bd39bc9145..d7a66dac3ad 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
@@ -284,7 +284,8 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_
Array<float3> positions(tot_samples);
get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions);
- bke::mesh_surface_sample::MeshAttributeInterpolator interp(mesh, positions, looptri_indices);
+ bke::mesh_surface_sample::MeshAttributeInterpolator interp(
+ mesh, IndexMask(tot_samples), positions, looptri_indices);
interp.sample_attribute(
src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
index 401a478f04c..6641e622362 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
@@ -250,7 +250,8 @@ static void raycast_from_points(const GeoNodeExecParams &params,
hit_distance_attribute.save();
/* Custom interpolated attributes */
- bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices);
+ bke::mesh_surface_sample::MeshAttributeInterpolator interp(
+ src_mesh, IndexMask(ray_origins.size()), hit_positions, hit_indices);
for (const int i : hit_attribute_names.index_range()) {
const std::optional<AttributeMetaData> meta_data = src_mesh_component->attribute_get_meta_data(
hit_attribute_names[i]);
@@ -304,7 +305,7 @@ static void geo_node_raycast_exec(GeoNodeExecParams params)
} // namespace blender::nodes
-void register_node_type_geo_raycast()
+void register_node_type_geo_legacy_raycast()
{
static bNodeType ntype;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
new file mode 100644
index 00000000000..e5ed5c02090
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -0,0 +1,456 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_mesh_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_sample.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using namespace blender::bke::mesh_surface_sample;
+
+namespace blender::nodes {
+
+static void geo_node_raycast_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Target Geometry");
+
+ b.add_input<decl::Vector>("Attribute").hide_value().supports_field();
+ b.add_input<decl::Float>("Attribute", "Attribute_001").hide_value().supports_field();
+ b.add_input<decl::Color>("Attribute", "Attribute_002").hide_value().supports_field();
+ b.add_input<decl::Bool>("Attribute", "Attribute_003").hide_value().supports_field();
+ b.add_input<decl::Int>("Attribute", "Attribute_004").hide_value().supports_field();
+
+ b.add_input<decl::Vector>("Source Position").implicit_field();
+ b.add_input<decl::Vector>("Ray Direction").default_value({0.0f, 0.0f, 1.0f}).supports_field();
+ b.add_input<decl::Float>("Ray Length")
+ .default_value(100.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .supports_field();
+
+ b.add_output<decl::Bool>("Is Hit").dependent_field();
+ b.add_output<decl::Vector>("Hit Position").dependent_field();
+ b.add_output<decl::Vector>("Hit Normal").dependent_field();
+ b.add_output<decl::Float>("Hit Distance").dependent_field();
+
+ b.add_output<decl::Vector>("Attribute").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Float>("Attribute", "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Color>("Attribute", "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Bool>("Attribute", "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Int>("Attribute", "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6});
+}
+
+static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE);
+}
+
+static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast),
+ __func__);
+ data->mapping = GEO_NODE_RAYCAST_INTERPOLATED;
+ data->data_type = CD_PROP_FLOAT;
+ node->storage = data;
+}
+
+static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeGeometryRaycast &data = *(const NodeGeometryRaycast *)node->storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(data.data_type);
+
+ bNodeSocket *socket_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *socket_float = socket_vector->next;
+ bNodeSocket *socket_color4f = socket_float->next;
+ bNodeSocket *socket_boolean = socket_color4f->next;
+ bNodeSocket *socket_int32 = socket_boolean->next;
+
+ nodeSetSocketAvailability(socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(socket_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(socket_int32, data_type == CD_PROP_INT32);
+
+ bNodeSocket *out_socket_vector = (bNodeSocket *)BLI_findlink(&node->outputs, 4);
+ bNodeSocket *out_socket_float = out_socket_vector->next;
+ bNodeSocket *out_socket_color4f = out_socket_float->next;
+ bNodeSocket *out_socket_boolean = out_socket_color4f->next;
+ bNodeSocket *out_socket_int32 = out_socket_boolean->next;
+
+ nodeSetSocketAvailability(out_socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(out_socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(out_socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(out_socket_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(out_socket_int32, data_type == CD_PROP_INT32);
+}
+
+static eAttributeMapMode get_map_mode(GeometryNodeRaycastMapMode map_mode)
+{
+ switch (map_mode) {
+ case GEO_NODE_RAYCAST_INTERPOLATED:
+ return eAttributeMapMode::INTERPOLATED;
+ default:
+ case GEO_NODE_RAYCAST_NEAREST:
+ return eAttributeMapMode::NEAREST;
+ }
+}
+
+static void raycast_to_mesh(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,
+ int &hit_count)
+{
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4);
+ if (tree_data.tree == nullptr) {
+ free_bvhtree_from_mesh(&tree_data);
+ return;
+ }
+
+ for (const int i : mask) {
+ const float ray_length = ray_lengths[i];
+ const float3 ray_origin = ray_origins[i];
+ const float3 ray_direction = ray_directions[i].normalized();
+
+ BVHTreeRayHit hit;
+ hit.index = -1;
+ hit.dist = ray_length;
+ if (BLI_bvhtree_ray_cast(tree_data.tree,
+ ray_origin,
+ ray_direction,
+ 0.0f,
+ &hit,
+ tree_data.raycast_callback,
+ &tree_data) != -1) {
+ hit_count++;
+ if (!r_hit.is_empty()) {
+ r_hit[i] = hit.index >= 0;
+ }
+ if (!r_hit_indices.is_empty()) {
+ /* The caller must be able to handle invalid indices anyway, so don't clamp this value. */
+ r_hit_indices[i] = hit.index;
+ }
+ if (!r_hit_positions.is_empty()) {
+ r_hit_positions[i] = hit.co;
+ }
+ if (!r_hit_normals.is_empty()) {
+ r_hit_normals[i] = hit.no;
+ }
+ if (!r_hit_distances.is_empty()) {
+ r_hit_distances[i] = hit.dist;
+ }
+ }
+ else {
+ if (!r_hit.is_empty()) {
+ r_hit[i] = false;
+ }
+ if (!r_hit_indices.is_empty()) {
+ r_hit_indices[i] = -1;
+ }
+ if (!r_hit_positions.is_empty()) {
+ r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f);
+ }
+ if (!r_hit_normals.is_empty()) {
+ r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f);
+ }
+ if (!r_hit_distances.is_empty()) {
+ r_hit_distances[i] = ray_length;
+ }
+ }
+ }
+
+ /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */
+ BLI_assert(tree_data.cached);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+class RaycastFunction : public fn::MultiFunction {
+ private:
+ GeometrySet target_;
+ GeometryNodeRaycastMapMode mapping_;
+
+ /** The field for data evaluated on the target geometry. */
+ std::optional<GeometryComponentFieldContext> target_context_;
+ std::unique_ptr<FieldEvaluator> target_evaluator_;
+ const GVArray *target_data_ = nullptr;
+
+ /* Always evaluate the target domain data on the point domain. Eventually this could be
+ * exposed as an option or determined automatically from the field inputs in order to avoid
+ * losing information if the target field is on a different domain. */
+ const AttributeDomain domain_ = ATTR_DOMAIN_POINT;
+
+ fn::MFSignature signature_;
+
+ public:
+ RaycastFunction(GeometrySet target, GField src_field, GeometryNodeRaycastMapMode mapping)
+ : target_(std::move(target)), mapping_((GeometryNodeRaycastMapMode)mapping)
+ {
+ target_.ensure_owns_direct_data();
+ this->evaluate_target_field(std::move(src_field));
+ signature_ = create_signature();
+ this->set_signature(&signature_);
+ }
+
+ fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Geometry Proximity"};
+ signature.single_input<float3>("Source Position");
+ signature.single_input<float3>("Ray Direction");
+ signature.single_input<float>("Ray Length");
+ signature.single_output<bool>("Is Hit");
+ signature.single_output<float3>("Hit Position");
+ signature.single_output<float3>("Hit Normal");
+ signature.single_output<float>("Distance");
+ if (target_data_) {
+ signature.single_output("Attribute", target_data_->type());
+ }
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ /* Hit positions are always necessary for retrieving the attribute from the target if that
+ * output is required, so always retrieve a span from the evaluator in that case (it's
+ * expected that the evaluator is more likely to have a spare buffer that could be used). */
+ MutableSpan<float3> hit_positions =
+ (target_data_) ? params.uninitialized_single_output<float3>(4, "Hit Position") :
+ params.uninitialized_single_output_if_required<float3>(4, "Hit Position");
+
+ Array<int> hit_indices;
+ if (target_data_) {
+ hit_indices.reinitialize(mask.min_array_size());
+ }
+
+ BLI_assert(target_.has_mesh());
+ const Mesh &mesh = *target_.get_mesh_for_read();
+
+ int hit_count = 0;
+ raycast_to_mesh(mask,
+ mesh,
+ params.readonly_single_input<float3>(0, "Source Position"),
+ params.readonly_single_input<float3>(1, "Ray Direction"),
+ params.readonly_single_input<float>(2, "Ray Length"),
+ params.uninitialized_single_output_if_required<bool>(3, "Is Hit"),
+ hit_indices,
+ hit_positions,
+ params.uninitialized_single_output_if_required<float3>(5, "Hit Normal"),
+ params.uninitialized_single_output_if_required<float>(6, "Distance"),
+ hit_count);
+
+ IndexMask hit_mask;
+ Vector<int64_t> hit_mask_indices;
+ if (hit_count < mask.size()) {
+ /* Not all rays hit the target. Create a corrected mask to avoid transferring attribute data
+ * to invalid indices. An alternative would be handling -1 indices in a separate case in
+ * #MeshAttributeInterpolator, but since it already has an IndexMask in its constructor, it's
+ * simpler to use that. */
+ hit_mask_indices.reserve(hit_count);
+ for (const int64_t i : mask) {
+ if (hit_indices[i] != -1) {
+ hit_mask_indices.append(i);
+ }
+ hit_mask = IndexMask(hit_mask_indices);
+ }
+ }
+ else {
+ hit_mask = mask;
+ }
+
+ if (target_data_) {
+ GMutableSpan result = params.uninitialized_single_output_if_required(7, "Attribute");
+ if (!result.is_empty()) {
+ MeshAttributeInterpolator interp(&mesh, hit_mask, hit_positions, hit_indices);
+ result.type().fill_assign_indices(result.type().default_value(), result.data(), mask);
+ interp.sample_data(*target_data_, domain_, get_map_mode(mapping_), result);
+ }
+ }
+ }
+
+ private:
+ void evaluate_target_field(GField src_field)
+ {
+ if (!src_field) {
+ return;
+ }
+ const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
+ target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
+ const int domain_size = mesh_component.attribute_domain_size(domain_);
+ target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size);
+ target_evaluator_->add(std::move(src_field));
+ target_evaluator_->evaluate();
+ target_data_ = &target_evaluator_->get_evaluated(0);
+ }
+};
+
+static GField get_input_attribute_field(GeoNodeExecParams &params, const CustomDataType data_type)
+{
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ if (params.output_is_required("Attribute_001")) {
+ return params.extract_input<Field<float>>("Attribute_001");
+ }
+ break;
+ case CD_PROP_FLOAT3:
+ if (params.output_is_required("Attribute")) {
+ return params.extract_input<Field<float3>>("Attribute");
+ }
+ break;
+ case CD_PROP_COLOR:
+ if (params.output_is_required("Attribute_002")) {
+ return params.extract_input<Field<ColorGeometry4f>>("Attribute_002");
+ }
+ break;
+ case CD_PROP_BOOL:
+ if (params.output_is_required("Attribute_003")) {
+ return params.extract_input<Field<bool>>("Attribute_003");
+ }
+ break;
+ case CD_PROP_INT32:
+ if (params.output_is_required("Attribute_004")) {
+ return params.extract_input<Field<int>>("Attribute_004");
+ }
+ break;
+ default:
+ BLI_assert_unreachable();
+ }
+ return {};
+}
+
+static void output_attribute_field(GeoNodeExecParams &params, GField field)
+{
+ switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) {
+ case CD_PROP_FLOAT: {
+ params.set_output("Attribute_001", Field<float>(field));
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ params.set_output("Attribute", Field<float3>(field));
+ break;
+ }
+ case CD_PROP_COLOR: {
+ params.set_output("Attribute_002", Field<ColorGeometry4f>(field));
+ break;
+ }
+ case CD_PROP_BOOL: {
+ params.set_output("Attribute_003", Field<bool>(field));
+ break;
+ }
+ case CD_PROP_INT32: {
+ params.set_output("Attribute_004", Field<int>(field));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void geo_node_raycast_exec(GeoNodeExecParams params)
+{
+ GeometrySet target = params.extract_input<GeometrySet>("Target Geometry");
+ const NodeGeometryRaycast &data = *(const NodeGeometryRaycast *)params.node().storage;
+ const GeometryNodeRaycastMapMode mapping = static_cast<GeometryNodeRaycastMapMode>(data.mapping);
+ const CustomDataType data_type = static_cast<CustomDataType>(data.data_type);
+
+ auto return_default = [&]() {
+ params.set_output("Is Hit", fn::make_constant_field<bool>(false));
+ params.set_output("Hit Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
+ params.set_output("Hit Normal", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
+ params.set_output("Hit Distance", fn::make_constant_field<float>(0.0f));
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ output_attribute_field(params, fn::make_constant_field<T>(T()));
+ });
+ };
+
+ if (target.has_instances()) {
+ if (target.has_realized_data()) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("Only realized geometry is supported, instances will not be used"));
+ }
+ else {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Target target must contain realized data"));
+ return return_default();
+ }
+ }
+
+ if (target.is_empty()) {
+ return return_default();
+ }
+
+ if (!target.has_mesh()) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("The target geometry must contain a mesh"));
+ return return_default();
+ }
+
+ if (target.get_mesh_for_read()->totpoly == 0) {
+ params.error_message_add(NodeWarningType::Error, TIP_("The target mesh must have faces"));
+ return return_default();
+ }
+
+ GField field = get_input_attribute_field(params, data_type);
+ const bool do_attribute_transfer = bool(field);
+ Field<float3> position_field = params.extract_input<Field<float3>>("Source Position");
+ Field<float3> direction_field = params.extract_input<Field<float3>>("Ray Direction");
+ Field<float> length_field = params.extract_input<Field<float>>("Ray Length");
+
+ auto fn = std::make_unique<RaycastFunction>(std::move(target), std::move(field), mapping);
+ auto op = std::make_shared<FieldOperation>(FieldOperation(
+ std::move(fn),
+ {std::move(position_field), std::move(direction_field), std::move(length_field)}));
+
+ params.set_output("Is Hit", Field<bool>(op, 0));
+ params.set_output("Hit Position", Field<float3>(op, 1));
+ params.set_output("Hit Normal", Field<float3>(op, 2));
+ params.set_output("Hit Distance", Field<float>(op, 3));
+ if (do_attribute_transfer) {
+ output_attribute_field(params, GField(op, 4));
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_raycast()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ node_type_init(&ntype, blender::nodes::geo_node_raycast_init);
+ node_type_update(&ntype, blender::nodes::geo_node_raycast_update);
+ node_type_storage(
+ &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage);
+ ntype.declare = blender::nodes::geo_node_raycast_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_raycast_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
index 63792304726..4bf856698d9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
@@ -409,8 +409,8 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction {
Array<float3> sampled_positions(mask.min_array_size());
get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions);
- MeshAttributeInterpolator interp(&mesh, sampled_positions, looptri_indices);
- interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, mask, dst);
+ MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices);
+ interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, dst);
}
private: