Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_raycast.cc
| Context not available. | |||||
| static void geo_node_raycast_declare(NodeDeclarationBuilder &b) | static void geo_node_raycast_declare(NodeDeclarationBuilder &b) | ||||
| { | { | ||||
| b.add_input<decl::Geometry>("Geometry"); | b.add_input<decl::Vector>("Source Position").implicit_field(); | ||||
| b.add_input<decl::Geometry>("Target Geometry"); | b.add_input<decl::Geometry>("Target Geometry"); | ||||
| b.add_input<decl::String>("Ray Direction"); | b.add_input<decl::Vector>("Ray Direction").default_value({0.0f, 0.0f, 1.0f}).supports_field(); | ||||
| b.add_input<decl::Vector>("Ray Direction", "Ray Direction_001") | b.add_input<decl::Float>("Ray Length") | ||||
| .default_value({0.0f, 0.0f, 1.0f}); | |||||
| b.add_input<decl::String>("Ray Length"); | |||||
| b.add_input<decl::Float>("Ray Length", "Ray Length_001") | |||||
| .default_value(100.0f) | .default_value(100.0f) | ||||
| .min(0.0f) | .min(0.0f) | ||||
| .subtype(PROP_DISTANCE); | .subtype(PROP_DISTANCE) | ||||
| b.add_input<decl::String>("Target Attribute"); | .supports_field(); | ||||
| b.add_input<decl::String>("Is Hit"); | |||||
| b.add_input<decl::String>("Hit Position"); | b.add_output<decl::Bool>("Is Hit").dependent_field(); | ||||
| b.add_input<decl::String>("Hit Normal"); | b.add_output<decl::Vector>("Hit Position").dependent_field(); | ||||
| b.add_input<decl::String>("Hit Distance"); | b.add_output<decl::Vector>("Hit Normal").dependent_field(); | ||||
| b.add_input<decl::String>("Hit Attribute"); | b.add_output<decl::Float>("Hit Distance").dependent_field(); | ||||
| b.add_output<decl::Geometry>("Geometry"); | |||||
| /* FOR TARGET ATTRIBUTE IF IMPLEMENTED*/ | |||||
| // b.add_output<decl::String>("Target Attribute"); | |||||
| // b.add_output<decl::String>("Hit Attribute"); | |||||
| } | } | ||||
JacquesLucke: These dependent fields need to specify what inputs they depend on. | |||||
| static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) | static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) | ||||
| Context not available. | |||||
| uiLayoutSetPropSep(layout, true); | uiLayoutSetPropSep(layout, true); | ||||
| uiLayoutSetPropDecorate(layout, false); | uiLayoutSetPropDecorate(layout, false); | ||||
| uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); | 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) | static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) | ||||
| { | { | ||||
| NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), | NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), | ||||
| __func__); | __func__); | ||||
| data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; | data->mapping = GEO_NODE_RAYCAST_INTERPOLATED; | ||||
| data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; | |||||
| node->storage = data; | node->storage = data; | ||||
| } | } | ||||
| static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) | static void raycast_to_mesh(IndexMask mask, | ||||
| { | const Mesh &mesh, | ||||
| NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; | |||||
| update_attribute_input_socket_availabilities( | |||||
| *node, | |||||
| "Ray Direction", | |||||
| (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction); | |||||
| update_attribute_input_socket_availabilities( | |||||
| *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); | |||||
| } | |||||
| static void raycast_to_mesh(const Mesh &mesh, | |||||
| const VArray<float3> &ray_origins, | const VArray<float3> &ray_origins, | ||||
| const VArray<float3> &ray_directions, | const VArray<float3> &ray_directions, | ||||
| const VArray<float> &ray_lengths, | const VArray<float> &ray_lengths, | ||||
| const MutableSpan<bool> r_hit, | const MutableSpan<bool> &r_hit, | ||||
| const MutableSpan<int> r_hit_indices, | const MutableSpan<int> &r_hit_indices, | ||||
| const MutableSpan<float3> r_hit_positions, | const MutableSpan<float3> &r_hit_positions, | ||||
| const MutableSpan<float3> r_hit_normals, | const MutableSpan<float3> &r_hit_normals, | ||||
| const MutableSpan<float> r_hit_distances) | const MutableSpan<float> &r_hit_distances) | ||||
| { | { | ||||
| BLI_assert(ray_origins.size() == ray_directions.size()); | BLI_assert(ray_origins.size() == ray_directions.size()); | ||||
| BLI_assert(ray_origins.size() == ray_lengths.size()); | BLI_assert(ray_origins.size() == ray_lengths.size()); | ||||
| Context not available. | |||||
| return; | return; | ||||
| } | } | ||||
| for (const int i : ray_origins.index_range()) { | for (const int i : mask) { | ||||
| const float ray_length = ray_lengths[i]; | const float ray_length = ray_lengths[i]; | ||||
| const float3 ray_origin = ray_origins[i]; | const float3 ray_origin = ray_origins[i]; | ||||
| const float3 ray_direction = ray_directions[i].normalized(); | const float3 ray_direction = ray_directions[i].normalized(); | ||||
| Context not available. | |||||
| free_bvhtree_from_mesh(&tree_data); | free_bvhtree_from_mesh(&tree_data); | ||||
| } | } | ||||
| static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( | static void raycast_from_points(IndexMask mask, | ||||
| GeometryNodeRaycastMapMode map_mode) | const VArray<float3> &ray_origins, | ||||
| { | const VArray<float3> &ray_directions, | ||||
| switch (map_mode) { | const VArray<float> &ray_lengths, | ||||
| 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 &target_geometry, | const GeometrySet &target_geometry, | ||||
| GeometryComponent &dst_component, | /*UNUSED FOR NOW MAY NEED FOR TARGET ATTRIBUTE NODE*/ | ||||
| const StringRef hit_name, | const GeometryNodeRaycastMapMode map_mode, | ||||
| const StringRef hit_position_name, | MutableSpan<bool> r_is_hits, | ||||
| const StringRef hit_normal_name, | MutableSpan<float3> r_hit_positions, | ||||
| const StringRef hit_distance_name, | MutableSpan<float3> r_hit_normals, | ||||
| const Span<std::string> hit_attribute_names, | MutableSpan<float> r_hit_distances) | ||||
| const Span<std::string> hit_attribute_output_names) | |||||
| { | { | ||||
| BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size()); | |||||
| const MeshComponent *src_mesh_component = | const MeshComponent *src_mesh_component = | ||||
| target_geometry.get_component_for_read<MeshComponent>(); | target_geometry.get_component_for_read<MeshComponent>(); | ||||
| if (src_mesh_component == nullptr) { | if (src_mesh_component == nullptr) { | ||||
| return; | return; | ||||
| } | } | ||||
| Context not available. | |||||
| return; | 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<int> hit_indices; | ||||
| Array<float3> hit_positions_internal; | Array<float3> hit_positions_internal; | ||||
| if (!hit_attribute_names.is_empty()) { | |||||
| hit_indices.reinitialize(ray_origins->size()); | |||||
| if (!hit_position_attribute) { | raycast_to_mesh(mask, | ||||
| hit_positions_internal.reinitialize(ray_origins->size()); | *src_mesh, | ||||
| } | |||||
| } | |||||
| 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_origins, | ||||
| ray_directions, | ray_directions, | ||||
| ray_lengths, | ray_lengths, | ||||
| is_hit, | r_is_hits, | ||||
| hit_indices, | hit_indices, | ||||
| hit_positions, | r_hit_positions, | ||||
| hit_normals, | r_hit_normals, | ||||
| hit_distances); | r_hit_distances); | ||||
| hit_attribute.save(); | /* THIS IS THE ORIGINAL CODE FOR TARGET ATTRIBUTE */ | ||||
| hit_position_attribute.save(); | |||||
| hit_normal_attribute.save(); | // const AttributeDomain result_domain = ATTR_DOMAIN_POINT; | ||||
| hit_distance_attribute.save(); | |||||
| // /* Custom interpolated attributes */ | |||||
| /* Custom interpolated attributes */ | // bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, | ||||
| bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices); | // hit_indices); for (const int i : hit_attribute_names.index_range()) { | ||||
| for (const int i : hit_attribute_names.index_range()) { | // const std::optional<AttributeMetaData> meta_data = | ||||
| const std::optional<AttributeMetaData> meta_data = src_mesh_component->attribute_get_meta_data( | // src_mesh_component->attribute_get_meta_data( | ||||
| hit_attribute_names[i]); | // hit_attribute_names[i]); | ||||
| if (meta_data) { | // if (meta_data) { | ||||
| ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read( | // ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read( | ||||
| hit_attribute_names[i]); | // hit_attribute_names[i]); | ||||
| OutputAttribute hit_attribute_output = dst_component.attribute_try_get_for_output_only( | // OutputAttribute hit_attribute_output = dst_component.attribute_try_get_for_output_only( | ||||
| hit_attribute_output_names[i], result_domain, meta_data->data_type); | // hit_attribute_output_names[i], result_domain, meta_data->data_type); | ||||
| interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode); | // interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode); | ||||
| hit_attribute_output.save(); | // hit_attribute_output.save(); | ||||
Not Done Inline Actionstypos (evaluate), space before Eventually JacquesLucke: typos (`evaluate`), space before `Eventually` | |||||
| } | // } | ||||
| } | // } | ||||
| } | } | ||||
| class RaycastFunction : public fn::MultiFunction { | |||||
| private: | |||||
| GeometrySet target_; | |||||
| GeometryNodeRaycastMapMode mode_; | |||||
| public: | |||||
| RaycastFunction(GeometrySet target, GeometryNodeRaycastMapMode mode) | |||||
| : target_(std::move(target)), mode_((GeometryNodeRaycastMapMode)mode) | |||||
| { | |||||
| static fn::MFSignature signature = create_signature(); | |||||
| this->set_signature(&signature); | |||||
| } | |||||
| static 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"); | |||||
| return signature.build(); | |||||
Done Inline ActionsJacquesLucke: The mask can't be ignored. This results in a crash in the following setup.
{F10618187}
I… | |||||
| } | |||||
| void call(IndexMask mask, | |||||
| fn::MFParams params, | |||||
| fn::MFContext UNUSED(context)) const override | |||||
| { | |||||
| const VArray<float3> &in_positions = params.readonly_single_input<float3>(0, | |||||
| "Source Position"); | |||||
| const VArray<float3> &in_directions = params.readonly_single_input<float3>(1, "Ray Direction"); | |||||
| const VArray<float> &in_lengths = params.readonly_single_input<float>(2, "Ray Length"); | |||||
| MutableSpan<bool> is_hits = params.uninitialized_single_output<bool>(3, "Is Hit"); | |||||
| MutableSpan<float3> hit_positions = params.uninitialized_single_output<float3>(4, | |||||
| "Hit Position"); | |||||
| MutableSpan<float3> hit_normals = params.uninitialized_single_output<float3>(5, "Hit Normal"); | |||||
| MutableSpan<float> hit_distances = params.uninitialized_single_output<float>(6, "Distance"); | |||||
| raycast_from_points(mask, | |||||
| in_positions, | |||||
| in_directions, | |||||
| in_lengths, | |||||
| target_, | |||||
| mode_, | |||||
| is_hits, | |||||
| hit_positions, | |||||
| hit_normals, | |||||
| hit_distances); | |||||
| } | |||||
| }; | |||||
| static void geo_node_raycast_exec(GeoNodeExecParams params) | static void geo_node_raycast_exec(GeoNodeExecParams params) | ||||
| { | { | ||||
| GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); | |||||
| GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); | GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); | ||||
| target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set); | |||||
Done Inline Actionsmissing dot JacquesLucke: missing dot | |||||
| const std::string hit_name = params.extract_input<std::string>("Is Hit"); | if (!target_geometry_set.has_mesh() && !target_geometry_set.has_pointcloud() && | ||||
| const std::string hit_position_name = params.extract_input<std::string>("Hit Position"); | !target_geometry_set.has_curve()) { | ||||
| const std::string hit_normal_name = params.extract_input<std::string>("Hit Normal"); | params.set_output("Is Hit", fn::make_constant_field<bool>(false)); | ||||
| const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance"); | 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})); | |||||
Done Inline ActionsLooks like this relies on the method implemented in another patch. JacquesLucke: Looks like this relies on the method implemented in another patch. | |||||
| params.set_output("Hit Distance", fn::make_constant_field<float>(0.0f)); | |||||
| return; | |||||
| } | |||||
| const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")}; | Field<float3> position_field = params.extract_input<Field<float3>>("Source Position"); | ||||
| const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")}; | Field<float3> direction_field = params.extract_input<Field<float3>>("Ray Direction"); | ||||
| Field<float> length_field = params.extract_input<Field<float>>("Ray Length"); | |||||
| geometry_set = bke::geometry_set_realize_instances(geometry_set); | const NodeGeometryRaycast &storage = *(const NodeGeometryRaycast *)params.node().storage; | ||||
| target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set); | |||||
| static const Array<GeometryComponentType> types = { | auto raycast_fn = std::make_unique<RaycastFunction>(std::move(target_geometry_set), | ||||
| GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; | (GeometryNodeRaycastMapMode)storage.mapping); | ||||
| for (const GeometryComponentType type : types) { | auto raycast_op = std::make_shared<FieldOperation>(FieldOperation( | ||||
| if (geometry_set.has(type)) { | std::move(raycast_fn), | ||||
| raycast_from_points(params, | {std::move(position_field), std::move(direction_field), std::move(length_field)})); | ||||
| target_geometry_set, | |||||
| geometry_set.get_component_for_write(type), | |||||
| hit_name, | |||||
| hit_position_name, | |||||
| hit_normal_name, | |||||
| hit_distance_name, | |||||
| hit_names, | |||||
| hit_output_names); | |||||
| } | |||||
| } | |||||
| params.set_output("Geometry", geometry_set); | params.set_output("Is Hit", Field<bool>(raycast_op, 0)); | ||||
| params.set_output("Hit Position", Field<float3>(raycast_op, 1)); | |||||
| params.set_output("Hit Normal", Field<float3>(raycast_op, 2)); | |||||
| params.set_output("Hit Distance", Field<float>(raycast_op, 3)); | |||||
| } | } | ||||
Done Inline ActionsYou have to pass the ray direction/length fields as input of the operation as well. JacquesLucke: You have to pass the ray direction/length fields as input of the operation as well. | |||||
Done Inline Actions
Thank you, this comment made me realize that the inputs and outputs mentioned in the call() function are for the operation, not the node. This makes things much clearer to me! guitargeek: > You have to pass the ray direction/length fields as input of the operation as well.
Thank… | |||||
| } // namespace blender::nodes | } // namespace blender::nodes | ||||
| Context not available. | |||||
| { | { | ||||
| static bNodeType ntype; | static bNodeType ntype; | ||||
| geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); | geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); | ||||
| node_type_size_preset(&ntype, NODE_SIZE_LARGE); | node_type_size_preset(&ntype, NODE_SIZE_LARGE); | ||||
| node_type_init(&ntype, blender::nodes::geo_node_raycast_init); | node_type_init(&ntype, blender::nodes::geo_node_raycast_init); | ||||
| node_type_update(&ntype, blender::nodes::geo_node_raycast_update); | |||||
| node_type_storage( | node_type_storage( | ||||
| &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); | &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); | ||||
| ntype.declare = blender::nodes::geo_node_raycast_declare; | ntype.declare = blender::nodes::geo_node_raycast_declare; | ||||
| Context not available. | |||||

These dependent fields need to specify what inputs they depend on.