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:
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: