Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
| #include "node_geometry_util.hh" | #include "node_geometry_util.hh" | ||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| #include "BKE_attribute_math.hh" | #include "BKE_attribute_math.hh" | ||||
| #include "BLI_task.hh" | #include "BLI_task.hh" | ||||
| #include "NOD_socket_search_link.hh" | #include "NOD_socket_search_link.hh" | ||||
| namespace blender::nodes { | |||||
| FieldAtIndexInput::FieldAtIndexInput(Field<int> index_field, | |||||
| GField value_field, | |||||
| eAttrDomain value_field_domain) | |||||
| : bke::GeometryFieldInput(value_field.cpp_type(), "Field at Index"), | |||||
| index_field_(std::move(index_field)), | |||||
| value_field_(std::move(value_field)), | |||||
| value_field_domain_(value_field_domain) | |||||
| { | |||||
| } | |||||
| GVArray FieldAtIndexInput::get_varray_for_context(const bke::GeometryFieldContext &context, | |||||
| const IndexMask mask) const | |||||
| { | |||||
| const std::optional<AttributeAccessor> attributes = context.attributes(); | |||||
| if (!attributes) { | |||||
| return {}; | |||||
| } | |||||
JacquesLucke: Don't do that, because `attributes()` may return none. | |||||
Done Inline ActionsThis is all copied, but I'll fix it here. HooglyBoogly: This is all copied, but I'll fix it here. | |||||
| const bke::GeometryFieldContext value_field_context{ | |||||
| context.geometry(), context.type(), value_field_domain_}; | |||||
| FieldEvaluator value_evaluator{value_field_context, | |||||
| attributes->domain_size(value_field_domain_)}; | |||||
| value_evaluator.add(value_field_); | |||||
| value_evaluator.evaluate(); | |||||
| const GVArray &values = value_evaluator.get_evaluated(0); | |||||
| FieldEvaluator index_evaluator{context, &mask}; | |||||
| index_evaluator.add(index_field_); | |||||
| index_evaluator.evaluate(); | |||||
| const VArray<int> indices = index_evaluator.get_evaluated<int>(0); | |||||
| GVArray output_array; | |||||
| attribute_math::convert_to_static_type(*type_, [&](auto dummy) { | |||||
| using T = decltype(dummy); | |||||
| Array<T> dst_array(mask.min_array_size()); | |||||
| VArray<T> src_values = values.typed<T>(); | |||||
| threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { | |||||
| for (const int i : mask.slice(range)) { | |||||
| const int index = indices[i]; | |||||
| if (src_values.index_range().contains(index)) { | |||||
| dst_array[i] = src_values[index]; | |||||
| } | |||||
| else { | |||||
| dst_array[i] = {}; | |||||
| } | |||||
| } | |||||
| }); | |||||
| output_array = VArray<T>::ForContainer(std::move(dst_array)); | |||||
| }); | |||||
| return output_array; | |||||
| } | |||||
| } // namespace blender::nodes | |||||
| namespace blender::nodes::node_geo_field_at_index_cc { | namespace blender::nodes::node_geo_field_at_index_cc { | ||||
| static void node_declare(NodeDeclarationBuilder &b) | static void node_declare(NodeDeclarationBuilder &b) | ||||
| { | { | ||||
| b.add_input<decl::Int>(N_("Index")).min(0).supports_field(); | b.add_input<decl::Int>(N_("Index")).min(0).supports_field(); | ||||
| b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field(); | b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field(); | ||||
| b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field(); | b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field(); | ||||
| ▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | if (type && *type != CD_PROP_STRING) { | ||||
| params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams ¶ms) { | params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams ¶ms) { | ||||
| bNode &node = params.add_node(node_type); | bNode &node = params.add_node(node_type); | ||||
| node.custom2 = *type; | node.custom2 = *type; | ||||
| params.update_and_connect_available_socket(node, "Value"); | params.update_and_connect_available_socket(node, "Value"); | ||||
| }); | }); | ||||
| } | } | ||||
| } | } | ||||
| class FieldAtIndex final : public bke::GeometryFieldInput { | |||||
| private: | |||||
| Field<int> index_field_; | |||||
| GField value_field_; | |||||
| eAttrDomain value_field_domain_; | |||||
| public: | |||||
| FieldAtIndex(Field<int> index_field, GField value_field, eAttrDomain value_field_domain) | |||||
| : bke::GeometryFieldInput(value_field.cpp_type(), "Field at Index"), | |||||
| index_field_(std::move(index_field)), | |||||
| value_field_(std::move(value_field)), | |||||
| value_field_domain_(value_field_domain) | |||||
| { | |||||
| } | |||||
| GVArray get_varray_for_context(const bke::GeometryFieldContext &context, | |||||
| const IndexMask mask) const final | |||||
| { | |||||
| const bke::GeometryFieldContext value_field_context{ | |||||
| context.geometry(), context.type(), value_field_domain_}; | |||||
| FieldEvaluator value_evaluator{value_field_context, | |||||
| context.attributes()->domain_size(value_field_domain_)}; | |||||
| value_evaluator.add(value_field_); | |||||
| value_evaluator.evaluate(); | |||||
| const GVArray &values = value_evaluator.get_evaluated(0); | |||||
| FieldEvaluator index_evaluator{context, &mask}; | |||||
| index_evaluator.add(index_field_); | |||||
| index_evaluator.evaluate(); | |||||
| const VArray<int> indices = index_evaluator.get_evaluated<int>(0); | |||||
| GVArray output_array; | |||||
| attribute_math::convert_to_static_type(*type_, [&](auto dummy) { | |||||
| using T = decltype(dummy); | |||||
| Array<T> dst_array(mask.min_array_size()); | |||||
| VArray<T> src_values = values.typed<T>(); | |||||
| threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { | |||||
| for (const int i : mask.slice(range)) { | |||||
| const int index = indices[i]; | |||||
| if (src_values.index_range().contains(index)) { | |||||
| dst_array[i] = src_values[index]; | |||||
| } | |||||
| else { | |||||
| dst_array[i] = {}; | |||||
| } | |||||
| } | |||||
| }); | |||||
| output_array = VArray<T>::ForContainer(std::move(dst_array)); | |||||
| }); | |||||
| return output_array; | |||||
| } | |||||
| std::optional<eAttrDomain> preferred_domain( | |||||
| const GeometryComponent & /*component*/) const override | |||||
| { | |||||
| return value_field_domain_; | |||||
| } | |||||
| }; | |||||
| static StringRefNull identifier_suffix(eCustomDataType data_type) | static StringRefNull identifier_suffix(eCustomDataType data_type) | ||||
| { | { | ||||
| switch (data_type) { | switch (data_type) { | ||||
| case CD_PROP_BOOL: | case CD_PROP_BOOL: | ||||
| return "Bool"; | return "Bool"; | ||||
| case CD_PROP_FLOAT: | case CD_PROP_FLOAT: | ||||
| return "Float"; | return "Float"; | ||||
| case CD_PROP_INT32: | case CD_PROP_INT32: | ||||
| Show All 14 Lines | static void node_geo_exec(GeoNodeExecParams params) | ||||
| const eAttrDomain domain = eAttrDomain(node.custom1); | const eAttrDomain domain = eAttrDomain(node.custom1); | ||||
| const eCustomDataType data_type = eCustomDataType(node.custom2); | const eCustomDataType data_type = eCustomDataType(node.custom2); | ||||
| Field<int> index_field = params.extract_input<Field<int>>("Index"); | Field<int> index_field = params.extract_input<Field<int>>("Index"); | ||||
| attribute_math::convert_to_static_type(data_type, [&](auto dummy) { | attribute_math::convert_to_static_type(data_type, [&](auto dummy) { | ||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||
| static const std::string identifier = "Value_" + identifier_suffix(data_type); | static const std::string identifier = "Value_" + identifier_suffix(data_type); | ||||
| Field<T> value_field = params.extract_input<Field<T>>(identifier); | Field<T> value_field = params.extract_input<Field<T>>(identifier); | ||||
| Field<T> output_field{ | Field<T> output_field{std::make_shared<FieldAtIndexInput>( | ||||
| std::make_shared<FieldAtIndex>(std::move(index_field), std::move(value_field), domain)}; | std::move(index_field), std::move(value_field), domain)}; | ||||
| params.set_output(identifier, std::move(output_field)); | params.set_output(identifier, std::move(output_field)); | ||||
| }); | }); | ||||
| } | } | ||||
| } // namespace blender::nodes::node_geo_field_at_index_cc | } // namespace blender::nodes::node_geo_field_at_index_cc | ||||
| void register_node_type_geo_field_at_index() | void register_node_type_geo_field_at_index() | ||||
| { | { | ||||
| Show All 13 Lines | |||||
Don't do that, because attributes() may return none.