Changeset View
Changeset View
Standalone View
Standalone View
source/blender/modifiers/intern/MOD_nodes.cc
| Show All 21 Lines | |||||
| */ | */ | ||||
| #include <cstring> | #include <cstring> | ||||
| #include <iostream> | #include <iostream> | ||||
| #include <string> | #include <string> | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_array.hh" | |||||
| #include "BLI_float3.hh" | #include "BLI_float3.hh" | ||||
| #include "BLI_listbase.h" | #include "BLI_listbase.h" | ||||
| #include "BLI_multi_value_map.hh" | #include "BLI_multi_value_map.hh" | ||||
| #include "BLI_set.hh" | #include "BLI_set.hh" | ||||
| #include "BLI_string.h" | #include "BLI_string.h" | ||||
| #include "BLI_string_search.h" | |||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "DNA_collection_types.h" | #include "DNA_collection_types.h" | ||||
| #include "DNA_defaults.h" | #include "DNA_defaults.h" | ||||
| #include "DNA_material_types.h" | #include "DNA_material_types.h" | ||||
| #include "DNA_mesh_types.h" | #include "DNA_mesh_types.h" | ||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "DNA_modifier_types.h" | #include "DNA_modifier_types.h" | ||||
| Show All 18 Lines | |||||
| #include "BKE_pointcloud.h" | #include "BKE_pointcloud.h" | ||||
| #include "BKE_screen.h" | #include "BKE_screen.h" | ||||
| #include "BKE_simulation.h" | #include "BKE_simulation.h" | ||||
| #include "BKE_workspace.h" | #include "BKE_workspace.h" | ||||
| #include "BLO_read_write.h" | #include "BLO_read_write.h" | ||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_interface.hh" | |||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_enum_types.h" | #include "RNA_enum_types.h" | ||||
| #include "DEG_depsgraph_build.h" | #include "DEG_depsgraph_build.h" | ||||
| #include "DEG_depsgraph_query.h" | #include "DEG_depsgraph_query.h" | ||||
| #include "MOD_modifiertypes.h" | #include "MOD_modifiertypes.h" | ||||
| #include "MOD_nodes.h" | #include "MOD_nodes.h" | ||||
| #include "MOD_nodes_evaluator.hh" | #include "MOD_nodes_evaluator.hh" | ||||
| #include "MOD_ui_common.h" | #include "MOD_ui_common.h" | ||||
| #include "ED_spreadsheet.h" | #include "ED_spreadsheet.h" | ||||
| #include "ED_undo.h" | |||||
| #include "NOD_derived_node_tree.hh" | #include "NOD_derived_node_tree.hh" | ||||
| #include "NOD_geometry.h" | #include "NOD_geometry.h" | ||||
| #include "NOD_geometry_nodes_eval_log.hh" | #include "NOD_geometry_nodes_eval_log.hh" | ||||
| #include "NOD_node_declaration.hh" | #include "NOD_node_declaration.hh" | ||||
| #include "FN_field.hh" | #include "FN_field.hh" | ||||
| #include "FN_multi_function.hh" | #include "FN_multi_function.hh" | ||||
| using blender::Array; | |||||
| using blender::ColorGeometry4f; | using blender::ColorGeometry4f; | ||||
| using blender::destruct_ptr; | using blender::destruct_ptr; | ||||
| using blender::float3; | using blender::float3; | ||||
| using blender::FunctionRef; | using blender::FunctionRef; | ||||
| using blender::IndexRange; | using blender::IndexRange; | ||||
| using blender::Map; | using blender::Map; | ||||
| using blender::Set; | using blender::Set; | ||||
| using blender::Span; | using blender::Span; | ||||
| using blender::StringRef; | using blender::StringRef; | ||||
| using blender::StringRefNull; | using blender::StringRefNull; | ||||
| using blender::Vector; | using blender::Vector; | ||||
| using blender::bke::OutputAttribute; | using blender::bke::OutputAttribute; | ||||
| using blender::fn::GField; | using blender::fn::GField; | ||||
| using blender::fn::GMutablePointer; | using blender::fn::GMutablePointer; | ||||
| using blender::fn::GPointer; | using blender::fn::GPointer; | ||||
| using blender::nodes::FieldInferencingInterface; | using blender::nodes::FieldInferencingInterface; | ||||
| using blender::nodes::GeoNodeExecParams; | using blender::nodes::GeoNodeExecParams; | ||||
| using blender::nodes::InputSocketFieldType; | using blender::nodes::InputSocketFieldType; | ||||
| using blender::threading::EnumerableThreadSpecific; | using blender::threading::EnumerableThreadSpecific; | ||||
| using namespace blender::fn::multi_function_types; | using namespace blender::fn::multi_function_types; | ||||
| using namespace blender::nodes::derived_node_tree_types; | using namespace blender::nodes::derived_node_tree_types; | ||||
| using geo_log::GeometryAttributeInfo; | |||||
| static void initData(ModifierData *md) | static void initData(ModifierData *md) | ||||
| { | { | ||||
| NodesModifierData *nmd = (NodesModifierData *)md; | NodesModifierData *nmd = (NodesModifierData *)md; | ||||
| BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(nmd, modifier)); | BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(nmd, modifier)); | ||||
| MEMCPY_STRUCT_AFTER(nmd, DNA_struct_default_get(NodesModifierData), modifier); | MEMCPY_STRUCT_AFTER(nmd, DNA_struct_default_get(NodesModifierData), modifier); | ||||
| ▲ Show 20 Lines • Show All 826 Lines • ▼ Show 20 Lines | for (const NodeRef *group_input_node : group_input_nodes) { | ||||
| for (const OutputSocketRef *socket : remaining_input_sockets) { | for (const OutputSocketRef *socket : remaining_input_sockets) { | ||||
| const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type(); | const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type(); | ||||
| void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); | void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); | ||||
| initialize_group_input(*nmd, *socket, value_in); | initialize_group_input(*nmd, *socket, value_in); | ||||
| group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); | group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); | ||||
| } | } | ||||
| } | } | ||||
| /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */ | |||||
| input_geometry_set.clear(); | |||||
| Vector<DInputSocket> group_outputs; | Vector<DInputSocket> group_outputs; | ||||
| for (const InputSocketRef *socket_ref : output_node.inputs().drop_back(1)) { | for (const InputSocketRef *socket_ref : output_node.inputs().drop_back(1)) { | ||||
| group_outputs.append({root_context, socket_ref}); | group_outputs.append({root_context, socket_ref}); | ||||
| } | } | ||||
| std::optional<geo_log::GeoLogger> geo_logger; | std::optional<geo_log::GeoLogger> geo_logger; | ||||
| blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params; | blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params; | ||||
| if (logging_enabled(ctx)) { | if (logging_enabled(ctx)) { | ||||
| Set<DSocket> preview_sockets; | Set<DSocket> preview_sockets; | ||||
| find_sockets_to_preview(nmd, ctx, tree, preview_sockets); | find_sockets_to_preview(nmd, ctx, tree, preview_sockets); | ||||
| eval_params.force_compute_sockets.extend(preview_sockets.begin(), preview_sockets.end()); | eval_params.force_compute_sockets.extend(preview_sockets.begin(), preview_sockets.end()); | ||||
| geo_logger.emplace(std::move(preview_sockets)); | geo_logger.emplace(std::move(preview_sockets)); | ||||
| geo_logger->log_input_geometry(input_geometry_set); | |||||
| } | } | ||||
| /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */ | |||||
| input_geometry_set.clear(); | |||||
| eval_params.input_values = group_inputs; | eval_params.input_values = group_inputs; | ||||
| eval_params.output_sockets = group_outputs; | eval_params.output_sockets = group_outputs; | ||||
| eval_params.mf_by_node = &mf_by_node; | eval_params.mf_by_node = &mf_by_node; | ||||
| eval_params.modifier_ = nmd; | eval_params.modifier_ = nmd; | ||||
| eval_params.depsgraph = ctx->depsgraph; | eval_params.depsgraph = ctx->depsgraph; | ||||
| eval_params.self_object = ctx->object; | eval_params.self_object = ctx->object; | ||||
| eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr; | eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr; | ||||
| blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); | blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); | ||||
| GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out<GeometrySet>(); | |||||
| if (geo_logger.has_value()) { | if (geo_logger.has_value()) { | ||||
| geo_logger->log_output_geometry(output_geometry_set); | |||||
| NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(&nmd->modifier); | NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(&nmd->modifier); | ||||
| clear_runtime_data(nmd_orig); | clear_runtime_data(nmd_orig); | ||||
| nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger); | nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger); | ||||
| } | } | ||||
| GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out<GeometrySet>(); | |||||
| for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) { | for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) { | ||||
| GMutablePointer socket_value = eval_params.r_output_values[socket->index()]; | GMutablePointer socket_value = eval_params.r_output_values[socket->index()]; | ||||
| store_output_value_in_geometry(output_geometry_set, nmd, *socket, socket_value); | store_output_value_in_geometry(output_geometry_set, nmd, *socket, socket_value); | ||||
| socket_value.destruct(); | socket_value.destruct(); | ||||
| } | } | ||||
| return output_geometry_set; | return output_geometry_set; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | |||||
| static void modifyGeometrySet(ModifierData *md, | static void modifyGeometrySet(ModifierData *md, | ||||
| const ModifierEvalContext *ctx, | const ModifierEvalContext *ctx, | ||||
| GeometrySet *geometry_set) | GeometrySet *geometry_set) | ||||
| { | { | ||||
| modifyGeometry(md, ctx, *geometry_set); | modifyGeometry(md, ctx, *geometry_set); | ||||
| } | } | ||||
| struct AttributeSearchData { | |||||
JacquesLucke: It's a bit sad that there is so much code duplication with `node_geometry_attribute_search.cc`. | |||||
| const geo_log::ModifierLog &modifier_log; | |||||
Not Done Inline ActionsIt's fine here, but just wanted to mention that I'm usually careful with using references in these trivial structs. That is because with references the implicit copy/move assignment operators are not generated. JacquesLucke: It's fine here, but just wanted to mention that I'm usually careful with using references in… | |||||
Done Inline ActionsIf I can get away with it, I like using references a little more, it just looks nicer. Thanks for the warning though. HooglyBoogly: If I can get away with it, I like using references a little more, it just looks nicer. Thanks… | |||||
| IDProperty &name_property; | |||||
| bool is_output; | |||||
| }; | |||||
| /* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */ | |||||
| BLI_STATIC_ASSERT(std::is_trivially_destructible_v<AttributeSearchData>, ""); | |||||
| static void attribute_search_update_fn(const bContext *UNUSED(C), | |||||
| void *arg, | |||||
| const char *str, | |||||
| uiSearchItems *items, | |||||
| const bool is_first) | |||||
| { | |||||
| AttributeSearchData *data = static_cast<AttributeSearchData *>(arg); | |||||
| const geo_log::GeometryValueLog &geometry_log = data->is_output ? | |||||
| data->modifier_log.output_geometry_log() : | |||||
| data->modifier_log.input_geometry_log(); | |||||
| Span<GeometryAttributeInfo> infos = geometry_log.attributes(); | |||||
| /* The shared attribute search code expects a span of pointers, so convert to that. */ | |||||
| Array<const GeometryAttributeInfo *> info_ptrs(infos.size()); | |||||
| for (const int i : infos.index_range()) { | |||||
| info_ptrs[i] = &infos[i]; | |||||
| } | |||||
| blender::ui::attribute_search_add_items( | |||||
| str, data->is_output, info_ptrs.as_span(), items, is_first); | |||||
| } | |||||
| static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) | |||||
| { | |||||
| if (item_v == nullptr) { | |||||
| return; | |||||
| } | |||||
| AttributeSearchData &data = *static_cast<AttributeSearchData *>(data_v); | |||||
| const GeometryAttributeInfo &item = *static_cast<const GeometryAttributeInfo *>(item_v); | |||||
| IDProperty &name_property = data.name_property; | |||||
| BLI_assert(name_property.type == IDP_STRING); | |||||
| IDP_AssignString(&name_property, item.name.c_str(), 0); | |||||
| ED_undo_push(C, "Assign Attribute Name"); | |||||
| } | |||||
| static void add_attribute_search_button(uiLayout *layout, | |||||
| const NodesModifierData &nmd, | |||||
| PointerRNA *md_ptr, | |||||
| const StringRefNull rna_path_attribute_name, | |||||
| const bNodeSocket &socket, | |||||
| const bool is_output) | |||||
| { | |||||
| const geo_log::ModifierLog *log = static_cast<geo_log::ModifierLog *>(nmd.runtime_eval_log); | |||||
| if (log == nullptr) { | |||||
| uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, "", ICON_NONE); | |||||
| return; | |||||
| } | |||||
| uiBlock *block = uiLayoutGetBlock(layout); | |||||
| uiBut *but = uiDefIconTextButR(block, | |||||
| UI_BTYPE_SEARCH_MENU, | |||||
| 0, | |||||
| ICON_NONE, | |||||
| "", | |||||
| 0, | |||||
| 0, | |||||
| 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */ | |||||
| UI_UNIT_Y, | |||||
| md_ptr, | |||||
| rna_path_attribute_name.c_str(), | |||||
| 0, | |||||
| 0.0f, | |||||
| 0.0f, | |||||
| 0.0f, | |||||
| 0.0f, | |||||
| ""); | |||||
| const std::string use_attribute_prop_name = socket.identifier + attribute_name_suffix; | |||||
| IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties, | |||||
| use_attribute_prop_name.c_str()); | |||||
| BLI_assert(property != nullptr); | |||||
| if (property == nullptr) { | |||||
| return; | |||||
| } | |||||
| AttributeSearchData *data = OBJECT_GUARDED_NEW(AttributeSearchData, | |||||
| {*log, *property, is_output}); | |||||
| UI_but_func_search_set_results_are_suggestions(but, true); | |||||
| #define MENU_SEP "\xe2\x96\xb6" | |||||
| UI_but_func_search_set_sep_string(but, MENU_SEP); | |||||
| UI_but_func_search_set(but, | |||||
| nullptr, | |||||
| attribute_search_update_fn, | |||||
| static_cast<void *>(data), | |||||
| true, | |||||
| nullptr, | |||||
| attribute_search_exec_fn, | |||||
| nullptr); | |||||
| } | |||||
| static void add_attribute_search_or_value_buttons(uiLayout *layout, | |||||
| const NodesModifierData &nmd, | |||||
| PointerRNA *md_ptr, | |||||
| const bNodeSocket &socket) | |||||
| { | |||||
| char socket_id_esc[sizeof(socket.identifier) * 2]; | |||||
| BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); | |||||
| const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]"; | |||||
| const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + | |||||
| use_attribute_suffix + "\"]"; | |||||
| const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + | |||||
| attribute_name_suffix + "\"]"; | |||||
| uiLayout *split = uiLayoutSplit(layout, 0.4f, false); | |||||
| uiLayout *name_row = uiLayoutRow(split, false); | |||||
| uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); | |||||
| uiItemL(name_row, socket.name, ICON_NONE); | |||||
| uiLayout *row = uiLayoutRow(split, true); | |||||
| PointerRNA props; | |||||
| uiItemFullO(row, | |||||
| "object.geometry_nodes_input_attribute_toggle", | |||||
| "", | |||||
| ICON_SPREADSHEET, | |||||
| nullptr, | |||||
| WM_OP_INVOKE_DEFAULT, | |||||
| 0, | |||||
| &props); | |||||
| RNA_string_set(&props, "modifier_name", nmd.modifier.name); | |||||
| RNA_string_set(&props, "prop_path", rna_path_use_attribute.c_str()); | |||||
| const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; | |||||
| if (use_attribute) { | |||||
| add_attribute_search_button(row, nmd, md_ptr, rna_path_attribute_name, socket, false); | |||||
| uiItemL(row, "", ICON_BLANK1); | |||||
| } | |||||
| else { | |||||
| uiItemR(row, md_ptr, rna_path.c_str(), 0, "", ICON_NONE); | |||||
| uiItemDecoratorR(row, md_ptr, rna_path.c_str(), 0); | |||||
| } | |||||
| } | |||||
| /* Drawing the properties manually with #uiItemR instead of #uiDefAutoButsRNA allows using | /* Drawing the properties manually with #uiItemR instead of #uiDefAutoButsRNA allows using | ||||
| * the node socket identifier for the property names, since they are unique, but also having | * the node socket identifier for the property names, since they are unique, but also having | ||||
| * the correct label displayed in the UI. */ | * the correct label displayed in the UI. */ | ||||
| static void draw_property_for_socket(uiLayout *layout, | static void draw_property_for_socket(uiLayout *layout, | ||||
| NodesModifierData *nmd, | NodesModifierData *nmd, | ||||
| PointerRNA *bmain_ptr, | PointerRNA *bmain_ptr, | ||||
| PointerRNA *md_ptr, | PointerRNA *md_ptr, | ||||
| const bNodeSocket &socket, | const bNodeSocket &socket, | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | case SOCK_TEXTURE: { | ||||
| break; | break; | ||||
| } | } | ||||
| case SOCK_IMAGE: { | case SOCK_IMAGE: { | ||||
| uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "images", socket.name, ICON_IMAGE); | uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "images", socket.name, ICON_IMAGE); | ||||
| break; | break; | ||||
| } | } | ||||
| default: { | default: { | ||||
| if (input_has_attribute_toggle(*nmd->node_group, socket_index)) { | if (input_has_attribute_toggle(*nmd->node_group, socket_index)) { | ||||
| const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + | add_attribute_search_or_value_buttons(layout, *nmd, md_ptr, socket); | ||||
| use_attribute_suffix + "\"]"; | |||||
| const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + | |||||
| attribute_name_suffix + "\"]"; | |||||
| uiLayout *row = uiLayoutRow(layout, true); | |||||
| const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; | |||||
| if (use_attribute) { | |||||
| uiItemR(row, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE); | |||||
| } | } | ||||
| else { | else { | ||||
| uiLayout *row = uiLayoutRow(layout, false); | |||||
| uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE); | uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE); | ||||
| } | uiItemDecoratorR(row, md_ptr, rna_path, 0); | ||||
| PointerRNA props; | |||||
| uiItemFullO(row, | |||||
| "object.geometry_nodes_input_attribute_toggle", | |||||
| "", | |||||
| ICON_SPREADSHEET, | |||||
| nullptr, | |||||
| WM_OP_INVOKE_DEFAULT, | |||||
| 0, | |||||
| &props); | |||||
| RNA_string_set(&props, "modifier_name", nmd->modifier.name); | |||||
| RNA_string_set(&props, "prop_path", rna_path_use_attribute.c_str()); | |||||
| } | |||||
| else { | |||||
| uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void draw_property_for_output_socket(uiLayout *layout, | static void draw_property_for_output_socket(uiLayout *layout, | ||||
| const NodesModifierData &nmd, | |||||
| PointerRNA *md_ptr, | PointerRNA *md_ptr, | ||||
| const bNodeSocket &socket) | const bNodeSocket &socket) | ||||
| { | { | ||||
| char socket_id_esc[sizeof(socket.identifier) * 2]; | char socket_id_esc[sizeof(socket.identifier) * 2]; | ||||
| BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); | BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); | ||||
| const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) + | const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) + | ||||
| attribute_name_suffix + "\"]"; | attribute_name_suffix + "\"]"; | ||||
| uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE); | uiLayout *split = uiLayoutSplit(layout, 0.4f, false); | ||||
| uiLayout *name_row = uiLayoutRow(split, false); | |||||
| uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); | |||||
| uiItemL(name_row, socket.name, ICON_NONE); | |||||
| uiLayout *row = uiLayoutRow(split, true); | |||||
| add_attribute_search_button(row, nmd, md_ptr, rna_path_attribute_name, socket, true); | |||||
| } | } | ||||
| static void panel_draw(const bContext *C, Panel *panel) | static void panel_draw(const bContext *C, Panel *panel) | ||||
| { | { | ||||
| uiLayout *layout = panel->layout; | uiLayout *layout = panel->layout; | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); | PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); | ||||
| NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data); | NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data); | ||||
| uiLayoutSetPropSep(layout, true); | uiLayoutSetPropSep(layout, true); | ||||
| uiLayoutSetPropDecorate(layout, true); | /* Decorators are added manually for supported properties because the | ||||
| * attribute/value toggle requires a manually built layout anyway. */ | |||||
| uiLayoutSetPropDecorate(layout, false); | |||||
| uiTemplateID(layout, | uiTemplateID(layout, | ||||
| C, | C, | ||||
| ptr, | ptr, | ||||
| "node_group", | "node_group", | ||||
| "node.new_geometry_node_group_assign", | "node.new_geometry_node_group_assign", | ||||
| nullptr, | nullptr, | ||||
| nullptr, | nullptr, | ||||
| ▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel) | ||||
| NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data); | NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data); | ||||
| uiLayoutSetPropSep(layout, true); | uiLayoutSetPropSep(layout, true); | ||||
| uiLayoutSetPropDecorate(layout, true); | uiLayoutSetPropDecorate(layout, true); | ||||
| if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { | if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { | ||||
| LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { | LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { | ||||
| if (socket_type_has_attribute_toggle(*socket)) { | if (socket_type_has_attribute_toggle(*socket)) { | ||||
| draw_property_for_output_socket(layout, ptr, *socket); | draw_property_for_output_socket(layout, *nmd, ptr, *socket); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void panelRegister(ARegionType *region_type) | static void panelRegister(ARegionType *region_type) | ||||
| { | { | ||||
| PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Nodes, panel_draw); | PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Nodes, panel_draw); | ||||
| ▲ Show 20 Lines • Show All 98 Lines • Show Last 20 Lines | |||||
It's a bit sad that there is so much code duplication with node_geometry_attribute_search.cc. Can you think about ways to reduce the duplication?