Changeset View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_points.cc
- This file was added.
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | |||||
| #include "BKE_pointcloud.h" | |||||
| #include "BLI_task.hh" | |||||
| #include "node_geometry_util.hh" | |||||
| namespace blender::nodes::node_geo_points_cc { | |||||
| static void node_declare(NodeDeclarationBuilder &b) | |||||
| { | |||||
| b.add_input<decl::Int>(N_("Count")) | |||||
| .default_value(1) | |||||
| .description(N_("The number of points to create")) | |||||
| .min(0); | |||||
| b.add_input<decl::Vector>(N_("Position")) | |||||
| .supports_field() | |||||
| .default_value(float3(0.0f)) | |||||
| .description(N_("The positions of the new points")); | |||||
| b.add_input<decl::Float>(N_("Radius")) | |||||
| .supports_field() | |||||
| .supports_field() | |||||
| .default_value(float(0.1f)) | |||||
| .description(N_("The radii of the new points")); | |||||
| b.add_output<decl::Geometry>(N_("Geometry")); | |||||
| } | |||||
| class PointsFieldContext : public FieldContext { | |||||
| private: | |||||
| int points_num_; | |||||
| public: | |||||
| PointsFieldContext(const int points_num) : points_num_(points_num) | |||||
HooglyBoogly: This can call `params.set_default_remaining_outputs()`, which is just a bit simpler. There… | |||||
| { | |||||
| } | |||||
| int64_t points_num() const | |||||
| { | |||||
Done Inline ActionsA couple empty lines to create sections would make this block easier to read IMO. Also, sometimes I call PointCloudComponent variables just points so retrieving the component fits on a single line. HooglyBoogly: A couple empty lines to create sections would make this block easier to read IMO.
Also… | |||||
| return points_num_; | |||||
| } | |||||
| GVArray get_varray_for_input(const FieldInput &field_input, | |||||
| const IndexMask mask, | |||||
| ResourceScope &UNUSED(scope)) const | |||||
| { | |||||
| const bke::IDAttributeFieldInput *id_field_input = | |||||
| dynamic_cast<const bke::IDAttributeFieldInput *>(&field_input); | |||||
Done Inline ActionsI'd suggest using add_with_destination and retrieving spans for that with the attribute API. I think this would be even simpler. HooglyBoogly: I'd suggest using `add_with_destination` and retrieving spans for that with the attribute API. | |||||
| const fn::IndexFieldInput *index_field_input = dynamic_cast<const fn::IndexFieldInput *>( | |||||
| &field_input); | |||||
| if (id_field_input == nullptr && index_field_input == nullptr) { | |||||
| return {}; | |||||
| } | |||||
Done Inline ActionsThe OutputAttributes have to be kept around in order to call save() after modifying them. This probably has some debug prints because it doesn't do that. HooglyBoogly: The `OutputAttribute`s have to be kept around in order to call `save()` after modifying them. | |||||
| return fn::IndexFieldInput::get_index_varray(mask); | |||||
| } | |||||
Done Inline ActionsIt doesn't really matter since this won't be used in the end, but a grain size of 1 is quite low for this. The grain size is the smallest task size TBB will create when looking for work. Generally it's chosen to lower overhead. If the work was very expensive, then a small grain size makes sense, but for simple work it usually makes sense to keep a larger grain size because the overhead from multi-threading becomes relatively more significant otherwise. HooglyBoogly: It doesn't really matter since this won't be used in the end, but a grain size of 1 is quite… | |||||
| }; | |||||
| static void node_geo_exec(GeoNodeExecParams params) | |||||
| { | |||||
| const int count = params.extract_input<int>("Count"); | |||||
Done Inline ActionsThis can reuse the implementation in IndexFieldInput::get_index_varray HooglyBoogly: This can reuse the implementation in `IndexFieldInput::get_index_varray` | |||||
Done Inline ActionsI meant actually calling IndexFieldInput::get_index_varray(mask); :) HooglyBoogly: I meant actually calling `IndexFieldInput::get_index_varray(mask);` :) | |||||
| if (count <= 0) { | |||||
| params.error_message_add(NodeWarningType::Warning, TIP_("Point count should be at least 1")); | |||||
| params.set_default_remaining_outputs(); | |||||
| return; | |||||
| } | |||||
| Field<float3> position_field = params.extract_input<Field<float3>>("Position"); | |||||
| Field<float> radius_field = params.extract_input<Field<float>>("Radius"); | |||||
| PointCloud *new_point_cloud = BKE_pointcloud_new_nomain(count); | |||||
| GeometrySet geometry_set = GeometrySet::create_with_pointcloud(new_point_cloud); | |||||
| PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); | |||||
| OutputAttribute_Typed<float3> output_position = points.attribute_try_get_for_output_only<float3>( | |||||
| "position", ATTR_DOMAIN_POINT); | |||||
| OutputAttribute_Typed<float> output_radii = points.attribute_try_get_for_output_only<float>( | |||||
| "radius", ATTR_DOMAIN_POINT); | |||||
| PointsFieldContext context{count}; | |||||
| fn::FieldEvaluator evaluator{context, count}; | |||||
| evaluator.add_with_destination(position_field, output_position.as_span()); | |||||
| evaluator.add_with_destination(radius_field, output_radii.as_span()); | |||||
| evaluator.evaluate(); | |||||
| output_position.save(); | |||||
Done Inline ActionsCan remove this commented line HooglyBoogly: Can remove this commented line | |||||
| output_radii.save(); | |||||
| params.set_output("Geometry", std::move(geometry_set)); | |||||
| } | |||||
| } // namespace blender::nodes::node_geo_points_cc | |||||
| void register_node_type_geo_points() | |||||
| { | |||||
| namespace file_ns = blender::nodes::node_geo_points_cc; | |||||
Done Inline ActionsUse std::move for the field inputs to the evaluator and in params.set_output HooglyBoogly: Use `std::move` for the field inputs to the evaluator and in `params.set_output` | |||||
| static bNodeType ntype; | |||||
| geo_node_type_base(&ntype, GEO_NODE_POINTS, "Points", NODE_CLASS_GEOMETRY); | |||||
| ntype.geometry_node_execute = file_ns::node_geo_exec; | |||||
| ntype.declare = file_ns::node_declare; | |||||
| nodeRegisterType(&ntype); | |||||
| } | |||||
This can call params.set_default_remaining_outputs(), which is just a bit simpler. There might as well be some warning like other primitive nodes as well.