diff options
Diffstat (limited to 'source/blender/nodes')
-rw-r--r-- | source/blender/nodes/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/nodes/NOD_geometry.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/NOD_static_types.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc | 82 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_raycast.cc | 321 |
5 files changed, 332 insertions, 74 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index d5ed8317bbb..39f26737620 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -193,6 +193,7 @@ set(SRC geometry/nodes/node_geo_point_separate.cc geometry/nodes/node_geo_point_translate.cc geometry/nodes/node_geo_points_to_volume.cc + geometry/nodes/node_geo_raycast.cc geometry/nodes/node_geo_select_by_material.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_subdivide.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 5bbdf57f1fa..fddaaf6e640 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -81,6 +81,7 @@ void register_node_type_geo_point_scale(void); void register_node_type_geo_point_separate(void); void register_node_type_geo_point_translate(void); void register_node_type_geo_points_to_volume(void); +void register_node_type_geo_raycast(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_material(void); void register_node_type_geo_separate_components(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 5e66b72cadb..d79fa7bffb3 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -320,6 +320,7 @@ DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "") DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc index 4b677dc5c82..d1114713672 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc @@ -102,14 +102,6 @@ static void get_result_domain_and_data_type(const GeometrySet &src_geometry, } } -static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) -{ - /* This only updates a cache and can be considered to be logically const. */ - const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh)); - const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); - return {looptris, looptris_len}; -} - static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, const VArray<float3> &positions, const MutableSpan<int> r_indices, @@ -212,7 +204,7 @@ static void get_closest_mesh_polygons(const Mesh &mesh, Array<int> looptri_indices(positions.size()); get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions); - Span<MLoopTri> looptris = get_mesh_looptris(mesh); + Span<MLoopTri> looptris = bke::mesh_surface_sample::get_mesh_looptris(mesh); for (const int i : positions.index_range()) { const MLoopTri &looptri = looptris[looptri_indices[i]]; r_poly_indices[i] = looptri.poly; @@ -262,32 +254,6 @@ static void get_closest_mesh_corners(const Mesh &mesh, } } -static void get_barycentric_coords(const Mesh &mesh, - const Span<int> looptri_indices, - const Span<float3> positions, - const MutableSpan<float3> r_bary_coords) -{ - BLI_assert(r_bary_coords.size() == positions.size()); - BLI_assert(r_bary_coords.size() == looptri_indices.size()); - - Span<MLoopTri> looptris = get_mesh_looptris(mesh); - - for (const int i : r_bary_coords.index_range()) { - const int looptri_index = looptri_indices[i]; - const MLoopTri &looptri = looptris[looptri_index]; - - const int v0_index = mesh.mloop[looptri.tri[0]].v; - const int v1_index = mesh.mloop[looptri.tri[1]].v; - const int v2_index = mesh.mloop[looptri.tri[2]].v; - - interp_weights_tri_v3(r_bary_coords[i], - mesh.mvert[v0_index].co, - mesh.mvert[v1_index].co, - mesh.mvert[v2_index].co, - positions[i]); - } -} - static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry, GeometryComponent &dst_component, const VArray<float3> &dst_positions, @@ -308,8 +274,11 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_ if (mesh->totpoly == 0) { return; } + ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type); - if (!src_attribute) { + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + dst_name, dst_domain, data_type); + if (!src_attribute || !dst_attribute) { return; } @@ -318,45 +287,10 @@ 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); - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - dst_name, dst_domain, data_type); - if (!dst_attribute) { - return; - } - GMutableSpan dst_span = dst_attribute.as_span(); - Array<float3> bary_coords; + bke::mesh_surface_sample::MeshAttributeInterpolator interp(mesh, positions, looptri_indices); + interp.sample_attribute( + src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED); - /* Compute barycentric coordinates only when they are needed. */ - if (src_attribute.domain != ATTR_DOMAIN_FACE) { - bary_coords.reinitialize(tot_samples); - get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords); - } - /* Interpolate the source attribute on the surface. */ - switch (src_attribute.domain) { - case ATTR_DOMAIN_POINT: { - bke::mesh_surface_sample::sample_point_attribute( - *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span); - break; - } - case ATTR_DOMAIN_FACE: { - bke::mesh_surface_sample::sample_face_attribute( - *mesh, looptri_indices, *src_attribute.varray, dst_span); - break; - } - case ATTR_DOMAIN_CORNER: { - bke::mesh_surface_sample::sample_corner_attribute( - *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span); - break; - } - case ATTR_DOMAIN_EDGE: { - /* Not yet supported. */ - break; - } - default: { - BLI_assert_unreachable(); - break; - } - } dst_attribute.save(); } 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..3a4492d24d6 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -0,0 +1,321 @@ +/* + * 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_bvhutils.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_raycast_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_GEOMETRY, N_("Target Geometry")}, + {SOCK_STRING, N_("Ray Direction")}, + {SOCK_VECTOR, N_("Ray Direction"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_STRING, N_("Ray Length")}, + {SOCK_FLOAT, N_("Ray Length"), 100.0, 0.0, 0.0, 0.0, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_STRING, N_("Target Attribute")}, + {SOCK_STRING, N_("Is Hit")}, + {SOCK_STRING, N_("Hit Position")}, + {SOCK_STRING, N_("Hit Normal")}, + {SOCK_STRING, N_("Hit Distance")}, + {SOCK_STRING, N_("Hit Attribute")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_raycast_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); + uiItemR(layout, ptr, "input_type_ray_direction", 0, IFACE_("Ray Direction"), ICON_NONE); + uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE); +} + +static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), + __func__); + data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node->storage = data; +} + +static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; + blender::nodes::update_attribute_input_socket_availabilities( + *node, + "Ray Direction", + (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction); + blender::nodes::update_attribute_input_socket_availabilities( + *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); +} + +namespace blender::nodes { + +static void raycast_to_mesh(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) +{ + BLI_assert(ray_origins.size() == ray_directions.size()); + BLI_assert(ray_origins.size() == ray_lengths.size()); + BLI_assert(ray_origins.size() == r_hit.size() || r_hit.is_empty()); + BLI_assert(ray_origins.size() == r_hit_indices.size() || r_hit_indices.is_empty()); + BLI_assert(ray_origins.size() == r_hit_positions.size() || r_hit_positions.is_empty()); + BLI_assert(ray_origins.size() == r_hit_normals.size() || r_hit_normals.is_empty()); + BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty()); + + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(mesh), BVHTREE_FROM_LOOPTRI, 4); + + if (tree_data.tree != NULL) { + for (const int i : ray_origins.index_range()) { + 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) { + if (!r_hit.is_empty()) { + r_hit[i] = hit.index >= 0; + } + if (!r_hit_indices.is_empty()) { + /* Index should always be a valid looptri index, use 0 when hit failed. */ + r_hit_indices[i] = max_ii(hit.index, 0); + } + 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] = 0; + } + 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; + } + } + } + + free_bvhtree_from_mesh(&tree_data); + } +} + +static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( + GeometryNodeRaycastMapMode map_mode) +{ + switch (map_mode) { + case GEO_NODE_RAYCAST_INTERPOLATED: + return bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED; + default: + case GEO_NODE_RAYCAST_NEAREST: + return bke::mesh_surface_sample::eAttributeMapMode::NEAREST; + } +} + +static void raycast_from_points(const GeoNodeExecParams ¶ms, + const GeometrySet &src_geometry, + GeometryComponent &dst_component, + const StringRef hit_name, + const StringRef hit_position_name, + const StringRef hit_normal_name, + const StringRef hit_distance_name, + const Span<std::string> hit_attribute_names, + const Span<std::string> hit_attribute_output_names) +{ + BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size()); + + const MeshComponent *src_mesh_component = src_geometry.get_component_for_read<MeshComponent>(); + if (src_mesh_component == nullptr) { + return; + } + const Mesh *src_mesh = src_mesh_component->get_for_read(); + if (src_mesh == nullptr) { + return; + } + if (src_mesh->totpoly == 0) { + return; + } + + const NodeGeometryRaycast &storage = *(const NodeGeometryRaycast *)params.node().storage; + bke::mesh_surface_sample::eAttributeMapMode map_mode = get_map_mode( + (GeometryNodeRaycastMapMode)storage.mapping); + const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + + GVArray_Typed<float3> ray_origins = dst_component.attribute_get_for_read<float3>( + "position", result_domain, {0, 0, 0}); + GVArray_Typed<float3> ray_directions = params.get_input_attribute<float3>( + "Ray Direction", dst_component, result_domain, {0, 0, 0}); + GVArray_Typed<float> ray_lengths = params.get_input_attribute<float>( + "Ray Length", dst_component, result_domain, 0); + + OutputAttribute_Typed<bool> hit_attribute = + dst_component.attribute_try_get_for_output_only<bool>(hit_name, result_domain); + OutputAttribute_Typed<float3> hit_position_attribute = + dst_component.attribute_try_get_for_output_only<float3>(hit_position_name, result_domain); + OutputAttribute_Typed<float3> hit_normal_attribute = + dst_component.attribute_try_get_for_output_only<float3>(hit_normal_name, result_domain); + OutputAttribute_Typed<float> hit_distance_attribute = + dst_component.attribute_try_get_for_output_only<float>(hit_distance_name, result_domain); + + /* Positions and looptri indices are always needed for interpolation, + * so create temporary arrays if no output attribute is given. + */ + Array<int> hit_indices; + Array<float3> hit_positions_internal; + if (!hit_attribute_names.is_empty()) { + hit_indices.reinitialize(ray_origins->size()); + + if (!hit_position_attribute) { + hit_positions_internal.reinitialize(ray_origins->size()); + } + } + const MutableSpan<bool> is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan<bool>(); + const MutableSpan<float3> hit_positions = hit_position_attribute ? + hit_position_attribute.as_span() : + hit_positions_internal; + const MutableSpan<float3> hit_normals = hit_normal_attribute ? hit_normal_attribute.as_span() : + MutableSpan<float3>(); + const MutableSpan<float> hit_distances = hit_distance_attribute ? + hit_distance_attribute.as_span() : + MutableSpan<float>(); + + raycast_to_mesh(src_mesh, + ray_origins, + ray_directions, + ray_lengths, + is_hit, + hit_indices, + hit_positions, + hit_normals, + hit_distances); + + hit_attribute.save(); + hit_position_attribute.save(); + hit_normal_attribute.save(); + hit_distance_attribute.save(); + + /* Custom interpolated attributes */ + bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, 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]); + if (meta_data) { + ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read( + hit_attribute_names[i]); + OutputAttribute hit_attribute_output = dst_component.attribute_try_get_for_output_only( + hit_attribute_output_names[i], result_domain, meta_data->data_type); + + interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode); + + hit_attribute_output.save(); + } + } +} + +static void geo_node_raycast_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet cast_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); + + const std::string hit_name = params.extract_input<std::string>("Is Hit"); + const std::string hit_position_name = params.extract_input<std::string>("Hit Position"); + const std::string hit_normal_name = params.extract_input<std::string>("Hit Normal"); + const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance"); + + const Array<std::string> hit_attribute_names = { + params.extract_input<std::string>("Target Attribute")}; + const Array<std::string> hit_attribute_output_names = { + params.extract_input<std::string>("Hit Attribute")}; + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + cast_geometry_set = bke::geometry_set_realize_instances(cast_geometry_set); + + static const Array<GeometryComponentType> SupportedTypes = { + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + for (GeometryComponentType geo_type : SupportedTypes) { + if (geometry_set.has(geo_type)) { + raycast_from_points(params, + cast_geometry_set, + geometry_set.get_component_for_write(geo_type), + hit_name, + hit_position_name, + hit_normal_name, + hit_distance_name, + hit_attribute_names, + hit_attribute_output_names); + } + } + + params.set_output("Geometry", geometry_set); +} + +} // 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_socket_templates(&ntype, geo_node_raycast_in, geo_node_raycast_out); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_init(&ntype, geo_node_raycast_init); + node_type_update(&ntype, geo_node_raycast_update); + node_type_storage( + &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec; + ntype.draw_buttons = geo_node_raycast_layout; + nodeRegisterType(&ntype); +} |