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"); | // b.add_output<decl::String>("Target Attribute"); | ||||
| // b.add_output<decl::String>("Hit Attribute"); | |||||
| } | } | ||||
| 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) | ||||
JacquesLucke: These dependent fields need to specify what inputs they depend on. | |||||
| 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 geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) | ||||
| { | { | ||||
| NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; | //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, | 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. | |||||
| } | } | ||||
| } | } | ||||
| static void raycast_from_points(const GeoNodeExecParams ¶ms, | static void raycast_from_points(const VArray<float3> &ray_origins, | ||||
| const VArray<float3> &ray_directions, | |||||
| const VArray<float> &ray_lengths, | |||||
| const GeometrySet &target_geometry, | const GeometrySet &target_geometry, | ||||
| GeometryComponent &dst_component, | const GeometryNodeRaycastMapMode map_mode, | ||||
| const StringRef hit_name, | MutableSpan<bool> r_is_hits, | ||||
| const StringRef hit_position_name, | MutableSpan<float3> r_hit_positions, | ||||
| const StringRef hit_normal_name, | MutableSpan<float3> r_hit_normals, | ||||
| const StringRef hit_distance_name, | MutableSpan<float> r_hit_distances) | ||||
| 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()); | // 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; | // const AttributeDomain result_domain = ATTR_DOMAIN_POINT; | ||||
| 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, | /* Positions and looptri indices are always needed for interpolation, | ||||
| * so create temporary arrays if no output attribute is given. */ | * 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) { | |||||
| 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, | 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(); | // /* Custom interpolated attributes */ | ||||
| hit_position_attribute.save(); | // bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, | ||||
Not Done Inline Actionstypos (evaluate), space before Eventually JacquesLucke: typos (`evaluate`), space before `Eventually` | |||||
| hit_normal_attribute.save(); | // hit_indices); for (const int i : hit_attribute_names.index_range()) { | ||||
| hit_distance_attribute.save(); | // const std::optional<AttributeMetaData> meta_data = | ||||
| // src_mesh_component->attribute_get_meta_data( | |||||
| /* Custom interpolated attributes */ | // hit_attribute_names[i]); | ||||
| bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices); | // if (meta_data) { | ||||
| for (const int i : hit_attribute_names.index_range()) { | // ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read( | ||||
| const std::optional<AttributeMetaData> meta_data = src_mesh_component->attribute_get_meta_data( | // hit_attribute_names[i]); | ||||
| hit_attribute_names[i]); | // OutputAttribute hit_attribute_output = dst_component.attribute_try_get_for_output_only( | ||||
| if (meta_data) { | // hit_attribute_output_names[i], result_domain, meta_data->data_type); | ||||
| ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read( | |||||
| hit_attribute_names[i]); | // interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode); | ||||
| 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.save(); | ||||
| // } | |||||
| interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode); | // } | ||||
| hit_attribute_output.save(); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| static void geo_node_raycast_exec(GeoNodeExecParams params) | class RaycastFunction : public fn::MultiFunction { | ||||
| { | private: | ||||
| GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); | GeometrySet target_; | ||||
| GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); | 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); | |||||
Done Inline ActionsJacquesLucke: The mask can't be ignored. This results in a crash in the following setup.
{F10618187}
I… | |||||
| } | |||||
| const std::string hit_name = params.extract_input<std::string>("Is Hit"); | static fn::MFSignature create_signature() | ||||
| 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"); | blender::fn::MFSignatureBuilder signature{"Geometry Proximity"}; | ||||
| const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance"); | 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(); | |||||
| } | |||||
| const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")}; | void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override | ||||
| const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")}; | { | ||||
| 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(in_positions, | |||||
| in_directions, | |||||
| in_lengths, | |||||
| target_, | |||||
| mode_, | |||||
| is_hits, | |||||
| hit_positions, | |||||
Done Inline Actionsmissing dot JacquesLucke: missing dot | |||||
| hit_normals, | |||||
| hit_distances); | |||||
| } | |||||
| }; | |||||
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. | |||||
| geometry_set = bke::geometry_set_realize_instances(geometry_set); | static void geo_node_raycast_exec(GeoNodeExecParams params) | ||||
| { | |||||
| GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); | |||||
| target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set); | target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set); | ||||
| static const Array<GeometryComponentType> types = { | if (!target_geometry_set.has_mesh() && !target_geometry_set.has_pointcloud() && | ||||
| GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; | !target_geometry_set.has_curve()) { | ||||
| for (const GeometryComponentType type : types) { | params.set_output("Is Hit", fn::make_constant_field<bool>(false)); | ||||
| if (geometry_set.has(type)) { | params.set_output("Hit Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f})); | ||||
| raycast_from_points(params, | params.set_output("Hit Normal", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f})); | ||||
| target_geometry_set, | params.set_output("Hit Distance", fn::make_constant_field<float>(0.0f)); | ||||
| geometry_set.get_component_for_write(type), | return; | ||||
| hit_name, | |||||
| hit_position_name, | |||||
| hit_normal_name, | |||||
| hit_distance_name, | |||||
| hit_names, | |||||
| hit_output_names); | |||||
| } | |||||
| } | } | ||||
| params.set_output("Geometry", geometry_set); | Field<float3> position_field = params.extract_input<Field<float3>>("Source Position"); | ||||
| const NodeGeometryRaycast &storage = *(const NodeGeometryRaycast *)params.node().storage; | |||||
| auto raycast_fn = std::make_unique<RaycastFunction>( | |||||
| std::move(target_geometry_set), (GeometryNodeRaycastMapMode)storage.mapping); | |||||
| auto raycast_op = std::make_shared<FieldOperation>( | |||||
| FieldOperation(std::move(raycast_fn), {std::move(position_field)})); | |||||
JacquesLuckeUnsubmitted 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. | |||||
guitargeekUnsubmitted 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… | |||||
| 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)); | |||||
| } | } | ||||
| } // 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_update(&ntype, blender::nodes::geo_node_raycast_update); | ||||
| Context not available. | |||||

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