Blender V4.3
node_geo_attribute_capture.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "NOD_rna_define.hh"
6
7#include "UI_interface.hh"
8#include "UI_resources.hh"
9
13
14#include "RNA_enum_types.hh"
15
16#include "BLO_read_write.hh"
17
18#include "BKE_screen.hh"
19
20#include "node_geometry_util.hh"
21
23
25
27{
28 const bNodeTree *tree = b.tree_or_null();
29 const bNode *node = b.node_or_null();
30 b.use_custom_socket_order();
31 b.allow_any_socket_order();
32
33 b.add_input<decl::Geometry>("Geometry");
34 b.add_output<decl::Geometry>("Geometry").propagate_all().align_with_previous();
35 if (node != nullptr) {
36 const NodeGeometryAttributeCapture &storage = node_storage(*node);
37 for (const NodeGeometryAttributeCaptureItem &item :
38 Span(storage.capture_items, storage.capture_items_num))
39 {
40 const eCustomDataType data_type = eCustomDataType(item.data_type);
41 const std::string input_identifier =
43 const std::string output_identifier =
45 b.add_input(data_type, item.name, input_identifier)
46 .field_on_all()
47 .socket_name_ptr(&tree->id, CaptureAttributeItemsAccessor::item_srna, &item, "name");
48 b.add_output(data_type, item.name, output_identifier).field_on_all().align_with_previous();
49 }
50 }
51 b.add_input<decl::Extend>("", "__extend__");
52 b.add_output<decl::Extend>("", "__extend__").align_with_previous();
53}
54
55static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
56{
57 uiLayoutSetPropSep(layout, true);
58 uiLayoutSetPropDecorate(layout, false);
59 uiItemR(layout, ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
60}
61
62static void node_init(bNodeTree * /*tree*/, bNode *node)
63{
64 NodeGeometryAttributeCapture *data = MEM_cnew<NodeGeometryAttributeCapture>(__func__);
65 data->domain = int8_t(AttrDomain::Point);
66 node->storage = data;
67}
68
69static void draw_item(uiList * /*ui_list*/,
70 const bContext *C,
71 uiLayout *layout,
72 PointerRNA * /*idataptr*/,
73 PointerRNA *itemptr,
74 int /*icon*/,
75 PointerRNA * /*active_dataptr*/,
76 const char * /*active_propname*/,
77 int /*index*/,
78 int /*flt_flag*/)
79{
80 uiLayout *row = uiLayoutRow(layout, true);
81 float4 color;
82 RNA_float_get_array(itemptr, "color", color);
83 uiTemplateNodeSocket(row, const_cast<bContext *>(C), color);
85 uiItemR(row, itemptr, "name", UI_ITEM_NONE, "", ICON_NONE);
86}
87
88static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
89{
90 static const uiListType *items_list = []() {
91 uiListType *list = MEM_cnew<uiListType>(__func__);
92 STRNCPY(list->idname, "NODE_UL_capture_items_list");
93 list->draw_item = draw_item;
95 return list;
96 }();
97
98 uiItemR(layout, ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
99
100 if (uiLayout *panel = uiLayoutPanel(
101 C, layout, "capture_attribute_items", false, TIP_("Capture Items")))
102 {
103 uiLayout *row = uiLayoutRow(panel, false);
104 uiTemplateList(row,
105 C,
106 items_list->idname,
107 "",
108 ptr,
109 "capture_items",
110 ptr,
111 "active_index",
112 nullptr,
113 3,
114 5,
116 0,
118 {
119 uiLayout *ops_col = uiLayoutColumn(row, false);
120 {
121 uiLayout *add_remove_col = uiLayoutColumn(ops_col, true);
122 uiItemO(add_remove_col, "", ICON_ADD, "node.capture_attribute_item_add");
123 uiItemO(add_remove_col, "", ICON_REMOVE, "node.capture_attribute_item_remove");
124 }
125 {
126 uiLayout *up_down_col = uiLayoutColumn(ops_col, true);
128 up_down_col, "node.capture_attribute_item_move", "", ICON_TRIA_UP, "direction", 0);
130 up_down_col, "node.capture_attribute_item_move", "", ICON_TRIA_DOWN, "direction", 1);
131 }
132 bNode &node = *static_cast<bNode *>(ptr->data);
133 auto &storage = node_storage(node);
134 if (storage.active_index >= 0 && storage.active_index < storage.capture_items_num) {
136 storage.capture_items[storage.active_index];
139 uiLayoutSetPropSep(panel, true);
140 uiLayoutSetPropDecorate(panel, false);
141 uiItemR(panel, &item_ptr, "data_type", UI_ITEM_NONE, nullptr, ICON_NONE);
142 }
143 }
144 }
145}
146
148{
150 ot, "Add Capture Attribute Item", __func__, "Add capture attribute item");
151}
152
154{
156 ot, "Remove Capture Attribute Item", __func__, "Remove active capture attribute item");
157}
158
160{
162 ot, "Move Capture Attribute Item", __func__, "Move active capture attribute item");
163}
164
171
172static void clean_unused_attributes(const AttributeFilter &attribute_filter,
173 const Set<StringRef> &keep,
174 GeometryComponent &component)
175{
176 std::optional<MutableAttributeAccessor> attributes = component.attributes_for_write();
177 if (!attributes.has_value()) {
178 return;
179 }
180
181 Vector<std::string> unused_ids;
182 attributes->foreach_attribute([&](const bke::AttributeIter &iter) {
184 return;
185 }
186 if (keep.contains(iter.name)) {
187 return;
188 }
189 if (!attribute_filter.allow_skip(iter.name)) {
190 return;
191 }
192 unused_ids.append(iter.name);
193 });
194
195 for (const std::string &unused_id : unused_ids) {
196 attributes->remove(unused_id);
197 }
198}
199
201{
202 GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
203
204 if (!params.output_is_required("Geometry")) {
205 params.error_message_add(
206 NodeWarningType::Info,
207 TIP_("The attribute output cannot be used without the geometry output"));
208 params.set_default_remaining_outputs();
209 return;
210 }
211
212 const NodeGeometryAttributeCapture &storage = node_storage(params.node());
213 const AttrDomain domain = AttrDomain(storage.domain);
214
216 Vector<GField> fields;
217 Vector<std::string> attribute_id_ptrs;
218 Set<StringRef> used_attribute_ids_set;
219 for (const NodeGeometryAttributeCaptureItem &item :
220 Span{storage.capture_items, storage.capture_items_num})
221 {
222 const std::string input_identifier =
224 const std::string output_identifier =
226 std::optional<std::string> attribute_id = params.get_output_anonymous_attribute_id_if_needed(
227 output_identifier);
228 if (!attribute_id) {
229 continue;
230 }
231 used_attribute_ids_set.add(*attribute_id);
232 fields.append(params.extract_input<GField>(input_identifier));
233 attribute_id_ptrs.append(std::move(*attribute_id));
234 used_items.append(&item);
235 }
236
237 if (fields.is_empty()) {
238 params.set_output("Geometry", geometry_set);
239 params.set_default_remaining_outputs();
240 return;
241 }
242
243 Array<StringRef> attribute_ids(attribute_id_ptrs.size());
244 for (const int i : attribute_id_ptrs.index_range()) {
245 attribute_ids[i] = attribute_id_ptrs[i];
246 }
247
248 const auto capture_on = [&](GeometryComponent &component) {
249 bke::try_capture_fields_on_geometry(component, attribute_ids, domain, fields);
250 /* Changing of the anonymous attributes may require removing attributes that are no longer
251 * needed. */
253 params.get_attribute_filter("Geometry"), used_attribute_ids_set, component);
254 };
255
256 /* Run on the instances component separately to only affect the top level of instances. */
257 if (domain == AttrDomain::Instance) {
258 if (geometry_set.has_instances()) {
260 }
261 }
262 else {
267
268 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
269 for (const GeometryComponent::Type type : types) {
270 if (geometry_set.has(type)) {
271 capture_on(geometry_set.get_component_for_write(type));
272 }
273 }
274 });
275 }
276
277 params.set_output("Geometry", geometry_set);
278}
279
280static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
281{
283 *ntree, *node, *node, *link);
284}
285
286static void node_free_storage(bNode *node)
287{
289 MEM_freeN(node->storage);
290}
291
292static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
293{
294 const NodeGeometryAttributeCapture &src_storage = node_storage(*src_node);
295 NodeGeometryAttributeCapture *dst_storage = MEM_cnew<NodeGeometryAttributeCapture>(__func__,
296 src_storage);
297 dst_node->storage = dst_storage;
298
300}
301
303{
304 const eNodeSocketDatatype type = eNodeSocketDatatype(params.other_socket().type);
305 if (type == SOCK_GEOMETRY) {
306 params.add_item(IFACE_("Geometry"), [](LinkSearchOpParams &params) {
307 bNode &node = params.add_node("GeometryNodeCaptureAttribute");
308 params.connect_available_socket(node, "Geometry");
309 });
310 }
312 return;
313 }
314
315 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
316 bNode &node = params.add_node("GeometryNodeCaptureAttribute");
318 node, type, params.socket.name);
319 params.update_and_connect_available_socket(node, params.socket.name);
320 });
321}
322
323static void node_register()
324{
325 static blender::bke::bNodeType ntype;
326
328 &ntype, GEO_NODE_CAPTURE_ATTRIBUTE, "Capture Attribute", NODE_CLASS_ATTRIBUTE);
330 &ntype, "NodeGeometryAttributeCapture", node_free_storage, node_copy_storage);
331 ntype.initfunc = node_init;
332 ntype.declare = node_declare;
340}
342
343} // namespace blender::nodes::node_geo_attribute_capture_cc
344
345namespace blender::nodes {
346
347StructRNA *CaptureAttributeItemsAccessor::item_srna = &RNA_NodeGeometryCaptureAttributeItem;
349
351{
352 const auto &storage = *static_cast<const NodeGeometryAttributeCapture *>(node.storage);
354 writer, NodeGeometryAttributeCaptureItem, storage.capture_items_num, storage.capture_items);
355 for (const NodeGeometryAttributeCaptureItem &item :
356 Span(storage.capture_items, storage.capture_items_num))
357 {
358 BLO_write_string(writer, item.name);
359 }
360}
361
363{
364 auto &storage = *static_cast<NodeGeometryAttributeCapture *>(node.storage);
366 reader, NodeGeometryAttributeCaptureItem, storage.capture_items_num, &storage.capture_items);
367 for (const NodeGeometryAttributeCaptureItem &item :
368 Span(storage.capture_items, storage.capture_items_num))
369 {
370 BLO_read_string(reader, &item.name);
371 }
372}
373
374} // namespace blender::nodes
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define GEO_NODE_CAPTURE_ATTRIBUTE
Definition BKE_node.hh:1210
#define NODE_CLASS_ATTRIBUTE
Definition BKE_node.hh:419
#define STRNCPY(dst, src)
Definition BLI_string.h:593
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:4992
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define BLO_write_struct_array(writer, struct_name, array_size, data_ptr)
#define BLO_read_struct_array(reader, struct_name, array_size, ptr_p)
#define TIP_(msgid)
#define IFACE_(msgid)
eNodeSocketDatatype
@ SOCK_GEOMETRY
@ UILST_LAYOUT_DEFAULT
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ UI_EMBOSS_NONE
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemEnumO(uiLayout *layout, const char *opname, const char *name, int icon, const char *propname, int value)
void uiTemplateList(uiLayout *layout, const bContext *C, const char *listtype_name, const char *list_id, PointerRNA *dataptr, const char *propname, PointerRNA *active_dataptr, const char *active_propname, const char *item_dyntip_propname, int rows, int maxrows, int layout_type, int columns, enum uiTemplateListFlags flags)
#define UI_ITEM_NONE
PanelLayout uiLayoutPanel(const bContext *C, uiLayout *layout, const char *idname, bool default_closed)
void uiTemplateNodeSocket(uiLayout *layout, bContext *C, const float color[4])
void uiLayoutSetEmboss(uiLayout *layout, eUIEmbossType emboss)
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
@ UI_TEMPLATE_LIST_FLAG_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
int64_t size() const
void append(const T &value)
void remove(const int64_t index)
IndexRange index_range() const
virtual std::optional< MutableAttributeAccessor > attributes_for_write()
local_group_size(16, 16) .push_constant(Type b
KDTree_3d * tree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static char ** types
Definition makesdna.cc:71
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
bool attribute_name_is_anonymous(const StringRef name)
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
bool try_capture_fields_on_geometry(MutableAttributeAccessor attributes, const fn::FieldContext &field_context, Span< StringRef > attribute_ids, AttrDomain domain, const fn::Field< bool > &selection, Span< fn::GField > fields)
static void draw_item(uiList *, const bContext *C, uiLayout *layout, PointerRNA *, PointerRNA *itemptr, int, PointerRNA *, const char *, int, int)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void node_geo_exec(GeoNodeExecParams params)
static void node_declare(NodeDeclarationBuilder &b)
static void clean_unused_attributes(const AttributeFilter &attribute_filter, const Set< StringRef > &keep, GeometryComponent &component)
static void NODE_OT_capture_attribute_item_remove(wmOperatorType *ot)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
static void NODE_OT_capture_attribute_item_add(wmOperatorType *ot)
static void NODE_OT_capture_attribute_item_move(wmOperatorType *ot)
static void node_copy_storage(bNodeTree *, bNode *dst_node, const bNode *src_node)
void move_active_item(wmOperatorType *ot, const char *name, const char *idname, const char *description)
void remove_active_item(wmOperatorType *ot, const char *name, const char *idname, const char *description)
void add_item(wmOperatorType *ot, const char *name, const char *idname, const char *description)
void copy_array(const bNode &src_node, bNode &dst_node)
Accessor::ItemT * add_item_with_socket_type_and_name(bNode &node, const eNodeSocketDatatype socket_type, const char *name)
bool try_add_item_via_any_extend_socket(bNodeTree &ntree, bNode &extend_node, bNode &storage_node, bNodeLink &link, const std::optional< StringRef > socket_identifier=std::nullopt)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
signed char int8_t
Definition stdint.h:75
NodeGeometryAttributeCaptureItem * capture_items
ID * owner_id
Definition RNA_types.hh:40
void * data
Definition RNA_types.hh:42
void * storage
bool allow_skip(const StringRef name) const
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
bool has(const GeometryComponent::Type component_type) const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:240
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:363
bool(* insert_link)(bNodeTree *ntree, bNode *node, bNodeLink *link)
Definition BKE_node.hh:309
NodeDeclareFunction declare
Definition BKE_node.hh:347
void(* register_operators)()
Definition BKE_node.hh:392
static bool supports_socket_type(const eNodeSocketDatatype socket_type)
static std::string input_socket_identifier_for_item(const NodeGeometryAttributeCaptureItem &item)
static void blend_read_data(BlendDataReader *reader, bNode &node)
static std::string output_socket_identifier_for_item(const NodeGeometryAttributeCaptureItem &item)
static void blend_write(BlendWriter *writer, const bNode &node)
char idname[BKE_ST_MAXNAME]
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
bool WM_uilisttype_add(uiListType *ult)