Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_input_data_fill.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_data_fill_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::Vector>("Replacement"); | |||||
| b.add_input<decl::Float>("Replacement", "Replacement_001"); | |||||
| b.add_input<decl::Int>("Replacement", "Replacement_002"); | |||||
| b.add_input<decl::Bool>("Replacement", "Replacement_003"); | |||||
| b.add_input<decl::Color>("Replacement", "Replacement_004"); | |||||
| b.add_input<decl::String>("Replacement", "Replacement_005"); | |||||
| b.add_input<decl::Int>("Start Offset").default_value(0); | |||||
| b.add_input<decl::Int>("End Offset").default_value(100); | |||||
| b.add_output<decl::Vector>("Value").dependent_field(); | |||||
| b.add_output<decl::Float>("Value", "Value_001").dependent_field(); | |||||
| b.add_output<decl::Int>("Value", "Value_002").dependent_field(); | |||||
| b.add_output<decl::Bool>("Value", "Value_003").dependent_field(); | |||||
| b.add_output<decl::Color>("Value", "Value_004").dependent_field(); | |||||
| b.add_output<decl::String>("Value", "Value_005").dependent_field(); | |||||
| } | |||||
| static void geo_node_data_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) | |||||
| { | |||||
| uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); | |||||
| } | |||||
| static void geo_node_data_fill_init(bNodeTree *UNUSED(tree), bNode *node) | |||||
| { | |||||
| NodeDataFill *data = (NodeDataFill *)MEM_callocN(sizeof(NodeDataFill), __func__); | |||||
| data->data_type = CD_PROP_FLOAT; | |||||
| node->storage = data; | |||||
| } | |||||
| static void geo_node_data_fill_update(bNodeTree *UNUSED(ntree), bNode *node) | |||||
| { | |||||
| const NodeDataFill &storage = *(const NodeDataFill *)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_replace_vector = sock_in_string->next; | |||||
| bNodeSocket *sock_replace_float = sock_replace_vector->next; | |||||
| bNodeSocket *sock_replace_int = sock_replace_float->next; | |||||
| bNodeSocket *sock_replace_bool = sock_replace_int->next; | |||||
| bNodeSocket *sock_replace_color = sock_replace_bool->next; | |||||
| bNodeSocket *sock_replace_string = sock_replace_color->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_replace_vector, data_type == CD_PROP_FLOAT3); | |||||
| nodeSetSocketAvailability(sock_replace_float, data_type == CD_PROP_FLOAT); | |||||
| nodeSetSocketAvailability(sock_replace_int, data_type == CD_PROP_INT32); | |||||
| nodeSetSocketAvailability(sock_replace_bool, data_type == CD_PROP_BOOL); | |||||
| nodeSetSocketAvailability(sock_replace_color, data_type == CD_PROP_COLOR); | |||||
| nodeSetSocketAvailability(sock_replace_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_data_fill_gvarray( | |||||
| const GeometryComponentFieldContext &field_context, | |||||
| const Field<T> &value_field, | |||||
| const T &replacement_value, | |||||
| int start_index, | |||||
| int end_index, | |||||
| 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); | |||||
| MutableSpan<T> mutable_values = values.as_mutable_span(); | |||||
| fn::FieldEvaluator evaluator{field_context, domain_size}; | |||||
| evaluator.add_with_destination(value_field, mutable_values); | |||||
| evaluator.evaluate(); | |||||
| if (end_index < start_index) { | |||||
| end_index = start_index; | |||||
| } | |||||
| if (start_index < 0) { | |||||
| start_index = 0; | |||||
| } | |||||
| else if (start_index >= values.size()) { | |||||
| start_index = values.size() - 1; | |||||
| } | |||||
| if (end_index < 0) { | |||||
| end_index = 0; | |||||
| } | |||||
| else if (end_index >= values.size()) { | |||||
| end_index = values.size() - 1; | |||||
| } | |||||
| for (int i = start_index; i <= end_index; i++) { | |||||
| mutable_values[i] = replacement_value; | |||||
| } | |||||
| return &scope.construct<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); | |||||
| } | |||||
| template<typename T> class DataFillFieldInput final : public fn::FieldInput { | |||||
| private: | |||||
| Field<T> input_; | |||||
| int start_; | |||||
| int end_; | |||||
| T replacement_; | |||||
| public: | |||||
| DataFillFieldInput(Field<T> input, T replacement, int start, int end) | |||||
| : fn::FieldInput(CPPType::get<T>(), "Data Fill"), | |||||
| input_(input), | |||||
| replacement_(replacement), | |||||
| start_(start), | |||||
| end_(end) | |||||
| { | |||||
| } | |||||
| 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_data_fill_gvarray<T>( | |||||
| *geometry_context, input_, replacement_, start_, end_, scope); | |||||
| } | |||||
| return nullptr; | |||||
| } | |||||
| uint64_t hash() const override | |||||
| { | |||||
| return get_default_hash_4<Field<T>, T, int, int>(input_, replacement_, start_, end_); | |||||
| } | |||||
| bool is_equal_to(const fn::FieldNode &other) const override | |||||
| { | |||||
| if (const DataFillFieldInput *other_data_fill = dynamic_cast<const DataFillFieldInput *>( | |||||
| &other)) { | |||||
| return input_ == other_data_fill->input_; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| }; | |||||
| static void geo_node_input_data_fill_exec(GeoNodeExecParams params) | |||||
| { | |||||
| const NodeDataFill &storage = *(const NodeDataFill *)params.node().storage; | |||||
| const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); | |||||
| int start_offset = params.extract_input<int>("Start Offset"); | |||||
| int end_offset = params.extract_input<int>("End Offset"); | |||||
| switch (data_type) { | |||||
| case CD_PROP_FLOAT3: { | |||||
| Field<float3> input_field = params.extract_input<Field<float3>>("Value"); | |||||
| float3 replacement = params.extract_input<float3>("Replacement"); | |||||
| Field<float3> fill_field{std::make_shared<DataFillFieldInput<float3>>( | |||||
| std::move(input_field), replacement, start_offset, end_offset)}; | |||||
| params.set_output("Value", std::move(fill_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_FLOAT: { | |||||
| Field<float> input_field = params.extract_input<Field<float>>("Value_001"); | |||||
| float replacement = params.extract_input<float>("Replacement_001"); | |||||
| Field<float> fill_field{std::make_shared<DataFillFieldInput<float>>( | |||||
| std::move(input_field), replacement, start_offset, end_offset)}; | |||||
| params.set_output("Value_001", std::move(fill_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_INT32: { | |||||
| Field<int> input_field = params.extract_input<Field<int>>("Value_002"); | |||||
| int replacement = params.extract_input<int>("Replacement_003"); | |||||
| Field<int> fill_field{std::make_shared<DataFillFieldInput<int>>( | |||||
| std::move(input_field), replacement, start_offset, end_offset)}; | |||||
| params.set_output("Value_002", std::move(fill_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_BOOL: { | |||||
| Field<bool> input_field = params.extract_input<Field<bool>>("Value_003"); | |||||
| bool replacement = params.extract_input<bool>("Replacement_003"); | |||||
| Field<bool> fill_field{std::make_shared<DataFillFieldInput<bool>>( | |||||
| std::move(input_field), replacement, start_offset, end_offset)}; | |||||
| params.set_output("Value_003", std::move(fill_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_COLOR: { | |||||
| Field<ColorGeometry4f> input_field = params.extract_input<Field<ColorGeometry4f>>( | |||||
| "Value_004"); | |||||
| ColorGeometry4f replacement = params.extract_input<ColorGeometry4f>("Replacement_004"); | |||||
| Field<int> fill_field{std::make_shared<DataFillFieldInput<ColorGeometry4f>>( | |||||
| std::move(input_field), replacement, start_offset, end_offset)}; | |||||
| params.set_output("Value_004", std::move(fill_field)); | |||||
| break; | |||||
| } | |||||
| case CD_PROP_STRING: { | |||||
| Field<std::string> input_field = params.extract_input<Field<std::string>>("Value_005"); | |||||
| std::string replacement = params.extract_input<std::string>("Replacement_005"); | |||||
| Field<std::string> fill_field{std::make_shared<DataFillFieldInput<std::string>>( | |||||
| std::move(input_field), replacement, start_offset, end_offset)}; | |||||
| params.set_output("Value_005", std::move(fill_field)); | |||||
| break; | |||||
| } | |||||
| default: { | |||||
| BLI_assert_unreachable(); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } // namespace blender::nodes | |||||
| void register_node_type_geo_input_data_fill() | |||||
| { | |||||
| static bNodeType ntype; | |||||
| geo_node_type_base(&ntype, GEO_NODE_INPUT_DATA_FILL, "Data Fill", NODE_CLASS_INPUT, 0); | |||||
| ntype.geometry_node_execute = blender::nodes::geo_node_input_data_fill_exec; | |||||
| node_type_init(&ntype, blender::nodes::geo_node_data_fill_init); | |||||
| node_type_update(&ntype, blender::nodes::geo_node_data_fill_update); | |||||
| ntype.draw_buttons = blender::nodes::geo_node_data_fill_layout; | |||||
| ntype.declare = blender::nodes::geo_node_data_fill_declare; | |||||
| node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); | |||||
| node_type_storage( | |||||
| &ntype, "NodeDataFill", node_free_standard_storage, node_copy_standard_storage); | |||||
| nodeRegisterType(&ntype); | |||||
| } | |||||