Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_input_index_offset.cc
- This file was added.
| /* | |||||
| * 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 "node_geometry_util.hh" | |||||
| #include "UI_interface.h" | |||||
| #include "UI_resources.h" | |||||
| namespace blender::nodes { | |||||
| static void geo_node_index_offset_declare(NodeDeclarationBuilder &b) | |||||
| { | |||||
| b.add_input<decl::Vector>("Value").supports_field().hide_value(); | |||||
| b.add_input<decl::Float>("Value", "Value_001").supports_field().hide_value(); | |||||
| b.add_input<decl::Int>("Value", "Value_002").supports_field().hide_value(); | |||||
| b.add_input<decl::Bool>("Value", "Value_003").supports_field().hide_value(); | |||||
| b.add_input<decl::Color>("Value", "Value_004").supports_field().hide_value(); | |||||
| b.add_input<decl::String>("Value", "Value_005").supports_field().hide_value(); | |||||
| b.add_input<decl::Int>("Offset").default_value(1).supports_field(); | |||||
| b.add_input<decl::Bool>("Clip").default_value(false).supports_field(); | |||||
| b.add_output<decl::Vector>("Offset Value").dependent_field(); | |||||
| b.add_output<decl::Float>("Offset Value", "Offset Value_001").dependent_field(); | |||||
| b.add_output<decl::Int>("Offset Value", "Offset Value_002").dependent_field(); | |||||
| b.add_output<decl::Bool>("Offset Value", "Offset Value_003").dependent_field(); | |||||
| b.add_output<decl::Color>("Offset Value", "Offset Value_004").dependent_field(); | |||||
| b.add_output<decl::String>("Offset Value", "Offset Value_005").dependent_field(); | |||||
| } | |||||
| static void geo_node_index_offset_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) | |||||
| { | |||||
| uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); | |||||
| } | |||||
| static void geo_node_index_offset_init(bNodeTree *UNUSED(tree), bNode *node) | |||||
| { | |||||
| NodeIndexOffset *data = (NodeIndexOffset *)MEM_callocN(sizeof(NodeIndexOffset), __func__); | |||||
| data->data_type = CD_PROP_FLOAT; | |||||
| node->storage = data; | |||||
| } | |||||
| static void geo_node_index_offset_update(bNodeTree *UNUSED(ntree), bNode *node) | |||||
| { | |||||
| const NodeIndexOffset &storage = *(const NodeIndexOffset *)node->storage; | |||||
| const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); | |||||
| bNodeSocket *sock_in_vector = (bNodeSocket *)node->inputs.first; | |||||
| bNodeSocket *sock_in_float = sock_in_vector->next; | |||||
| bNodeSocket *sock_in_int = sock_in_float->next; | |||||
| bNodeSocket *sock_in_bool = sock_in_int->next; | |||||
| bNodeSocket *sock_in_color = sock_in_bool->next; | |||||
| bNodeSocket *sock_in_string = sock_in_color->next; | |||||
| bNodeSocket *sock_in_offset = sock_in_string->next; | |||||
| bNodeSocket *sock_in_clip = sock_in_offset->next; | |||||
| bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first; | |||||
| bNodeSocket *sock_out_float = sock_out_vector->next; | |||||
| bNodeSocket *sock_out_int = sock_out_float->next; | |||||
| bNodeSocket *sock_out_bool = sock_out_int->next; | |||||
| bNodeSocket *sock_out_color = sock_out_bool->next; | |||||
| bNodeSocket *sock_out_string = sock_out_color->next; | |||||
| nodeSetSocketAvailability(sock_in_vector, data_type == CD_PROP_FLOAT3); | |||||
| nodeSetSocketAvailability(sock_in_float, data_type == CD_PROP_FLOAT); | |||||
| nodeSetSocketAvailability(sock_in_int, data_type == CD_PROP_INT32); | |||||
| nodeSetSocketAvailability(sock_in_bool, data_type == CD_PROP_BOOL); | |||||
| nodeSetSocketAvailability(sock_in_color, data_type == CD_PROP_COLOR); | |||||
| nodeSetSocketAvailability(sock_in_string, data_type == CD_PROP_STRING); | |||||
| nodeSetSocketAvailability(sock_out_vector, data_type == CD_PROP_FLOAT3); | |||||
| nodeSetSocketAvailability(sock_out_float, data_type == CD_PROP_FLOAT); | |||||
| nodeSetSocketAvailability(sock_out_int, data_type == CD_PROP_INT32); | |||||
| nodeSetSocketAvailability(sock_out_bool, data_type == CD_PROP_BOOL); | |||||
| nodeSetSocketAvailability(sock_out_color, data_type == CD_PROP_COLOR); | |||||
| nodeSetSocketAvailability(sock_out_string, data_type == CD_PROP_STRING); | |||||
| } | |||||
| template<typename T> | |||||
| static const GVArray *construct_index_offset_gvarray( | |||||
| const GeometryComponentFieldContext &field_context, | |||||
| const Field<T> &value_field, | |||||
| const Field<int> &offset_field, | |||||
| const Field<bool> &clip_field, | |||||
| ResourceScope &scope) | |||||
| { | |||||
| const GeometryComponent &component = field_context.geometry_component(); | |||||
| const int domain_size = component.attribute_domain_size(field_context.domain()); | |||||
| Array<T> values(domain_size); | |||||
| Array<int> offsets(domain_size); | |||||
| Array<bool> clips(domain_size); | |||||
| Array<T> values_out(domain_size); | |||||
| MutableSpan<T> mutable_out = values_out.as_mutable_span(); | |||||
| fn::FieldEvaluator evaluator{field_context, domain_size}; | |||||
| evaluator.add_with_destination(value_field, values.as_mutable_span()); | |||||
| evaluator.add_with_destination(offset_field, offsets.as_mutable_span()); | |||||
| evaluator.add_with_destination(clip_field, clips.as_mutable_span()); | |||||
| evaluator.evaluate(); | |||||
| for (const int i : IndexRange(values.size())) { | |||||
| int offset = i + offsets[i]; | |||||
| if (offset >= 0 && offset < values.size()) { | |||||
| mutable_out[i] = values[offset]; | |||||
| } | |||||
| else if (offset < 0) { | |||||
| mutable_out[i] = clips[i] ? values.first() : values[mod_i(offset, values.size())]; | |||||
| } | |||||
| else { | |||||
| mutable_out[i] = clips[i] ? values.last() : values[mod_i(offset, values.size())]; | |||||
| } | |||||
| } | |||||
| return &scope.construct<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values_out)); | |||||
| } | |||||
| template<typename T> class IndexOffsetFieldInput final : public fn::FieldInput { | |||||
| private: | |||||
| Field<T> input_; | |||||
| Field<int> offset_; | |||||
| Field<bool> clip_; | |||||
| public: | |||||
| IndexOffsetFieldInput(Field<T> input, Field<int> offset, Field<bool> clip) | |||||
| : fn::FieldInput(CPPType::get<T>(), "Offset Index"), | |||||
| input_(input), | |||||
| offset_(offset), | |||||
| clip_(clip) | |||||
| { | |||||
| } | |||||
| const GVArray *get_varray_for_context(const fn::FieldContext &context, | |||||
| IndexMask UNUSED(mask), | |||||
| ResourceScope &scope) const final | |||||
| { | |||||
| if (const GeometryComponentFieldContext *geometry_context = | |||||
| dynamic_cast<const GeometryComponentFieldContext *>(&context)) { | |||||
| return construct_index_offset_gvarray<T>(*geometry_context, input_, offset_, clip_, scope); | |||||
| } | |||||
| return nullptr; | |||||
| } | |||||
| uint64_t hash() const override | |||||
| { | |||||
| return get_default_hash_3<Field<T>, Field<int>, Field<bool>>(input_, offset_, clip_); | |||||
| } | |||||
| bool is_equal_to(const fn::FieldNode &other) const override | |||||
| { | |||||
| if (const IndexOffsetFieldInput *other_index_offset = | |||||
| dynamic_cast<const IndexOffsetFieldInput *>(&other)) { | |||||
| return input_ == other_index_offset->input_ && offset_ == other_index_offset->offset_ && | |||||
| clip_ == other_index_offset->clip_; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| }; | |||||
| static void geo_node_input_index_offset_exec(GeoNodeExecParams params) | |||||
| { | |||||
| const NodeIndexOffset &storage = *(const NodeIndexOffset *)params.node().storage; | |||||
| const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); | |||||
| Field<int> offset_value_field = params.extract_input<Field<int>>("Offset"); | |||||
| Field<bool> clip_value_field = params.extract_input<Field<bool>>("Clip"); | |||||
| switch (data_type) { | |||||
| case CD_PROP_FLOAT3: { | |||||
| Field<float3> input_field = params.extract_input<Field<float3>>("Value"); | |||||
| Field<float3> offset_field{std::make_shared<IndexOffsetFieldInput<float3>>( | |||||
| std::move(input_field), std::move(offset_value_field), std::move(clip_value_field))}; | |||||
| params.set_output("Offset Value", std::move(offset_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_FLOAT: { | |||||
| Field<float> input_field = params.extract_input<Field<float>>("Value_001"); | |||||
| Field<float> offset_field{std::make_shared<IndexOffsetFieldInput<float>>( | |||||
| std::move(input_field), std::move(offset_value_field), std::move(clip_value_field))}; | |||||
| params.set_output("Offset Value_001", std::move(offset_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_INT32: { | |||||
| Field<int> input_field = params.extract_input<Field<int>>("Value_002"); | |||||
| Field<int> offset_field{std::make_shared<IndexOffsetFieldInput<int>>( | |||||
| std::move(input_field), std::move(offset_value_field), std::move(clip_value_field))}; | |||||
| params.set_output("Offset Value_002", std::move(offset_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_BOOL: { | |||||
| Field<bool> input_field = params.extract_input<Field<bool>>("Value_003"); | |||||
| Field<bool> offset_field{std::make_shared<IndexOffsetFieldInput<bool>>( | |||||
| std::move(input_field), std::move(offset_value_field), std::move(clip_value_field))}; | |||||
| params.set_output("Offset Value_003", std::move(offset_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_COLOR: { | |||||
| Field<ColorGeometry4f> input_field = params.extract_input<Field<ColorGeometry4f>>( | |||||
| "Value_004"); | |||||
| Field<int> offset_field{std::make_shared<IndexOffsetFieldInput<ColorGeometry4f>>( | |||||
| std::move(input_field), std::move(offset_value_field), std::move(clip_value_field))}; | |||||
| params.set_output("Offset Value_004", std::move(offset_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_STRING: { | |||||
| Field<std::string> input_field = params.extract_input<Field<std::string>>("Value_005"); | |||||
| Field<std::string> offset_field{std::make_shared<IndexOffsetFieldInput<std::string>>( | |||||
| std::move(input_field), std::move(offset_value_field), std::move(clip_value_field))}; | |||||
| params.set_output("Offset Value_005", std::move(offset_field)); | |||||
| break; | |||||
| } | |||||
| default: { | |||||
| BLI_assert_unreachable(); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } // namespace blender::nodes | |||||
| void register_node_type_geo_input_index_offset() | |||||
| { | |||||
| static bNodeType ntype; | |||||
| geo_node_type_base(&ntype, GEO_NODE_INPUT_INDEX_OFFSET, "Offset Index", NODE_CLASS_INPUT, 0); | |||||
| ntype.geometry_node_execute = blender::nodes::geo_node_input_index_offset_exec; | |||||
| node_type_init(&ntype, blender::nodes::geo_node_index_offset_init); | |||||
| node_type_update(&ntype, blender::nodes::geo_node_index_offset_update); | |||||
| ntype.draw_buttons = blender::nodes::geo_node_index_offset_layout; | |||||
| ntype.declare = blender::nodes::geo_node_index_offset_declare; | |||||
| node_type_storage( | |||||
| &ntype, "NodeIndexOffset", node_free_standard_storage, node_copy_standard_storage); | |||||
| nodeRegisterType(&ntype); | |||||
| } | |||||