Blender V4.5
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 "UI_interface.hh"
6#include "UI_resources.hh"
7
13
14#include "RNA_enum_types.hh"
15
16#include "BLO_read_write.hh"
17
18#include "BKE_library.hh"
19#include "BKE_screen.hh"
20
21#include "node_geometry_util.hh"
22
24
26
28{
29 const bNodeTree *tree = b.tree_or_null();
30 const bNode *node = b.node_or_null();
31 b.use_custom_socket_order();
32 b.allow_any_socket_order();
33
34 b.add_default_layout();
35
36 b.add_input<decl::Geometry>("Geometry");
37 b.add_output<decl::Geometry>("Geometry").propagate_all().align_with_previous();
38 if (node != nullptr) {
39 const NodeGeometryAttributeCapture &storage = node_storage(*node);
40 for (const NodeGeometryAttributeCaptureItem &item :
41 Span(storage.capture_items, storage.capture_items_num))
42 {
43 const eCustomDataType data_type = eCustomDataType(item.data_type);
44 const std::string input_identifier =
46 const std::string output_identifier =
48 b.add_input(data_type, item.name, input_identifier)
49 .field_on_all()
50 .socket_name_ptr(&tree->id, CaptureAttributeItemsAccessor::item_srna, &item, "name");
51 b.add_output(data_type, item.name, output_identifier).field_on_all().align_with_previous();
52 }
53 }
54 b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Field);
55 b.add_output<decl::Extend>("", "__extend__")
56 .structure_type(StructureType::Field)
57 .align_with_previous();
58}
59
60static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
61{
62 uiLayoutSetPropSep(layout, true);
63 uiLayoutSetPropDecorate(layout, false);
64 layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
65}
66
67static void node_init(bNodeTree * /*tree*/, bNode *node)
68{
70 data->domain = int8_t(AttrDomain::Point);
71 node->storage = data;
72}
73
75{
76 bNodeTree &tree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
77 bNode &node = *static_cast<bNode *>(ptr->data);
78
79 layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
80
81 if (uiLayout *panel = layout->panel(
82 C, "capture_attribute_items", false, IFACE_("Capture Items")))
83 {
85 C, panel, tree, node);
87 tree, node, [&](PointerRNA *item_ptr) {
88 uiLayoutSetPropSep(panel, true);
89 uiLayoutSetPropDecorate(panel, false);
90 panel->prop(item_ptr, "data_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
91 });
92 }
93}
94
99
100static void clean_unused_attributes(const AttributeFilter &attribute_filter,
101 const Set<StringRef> &keep,
102 GeometryComponent &component)
103{
104 std::optional<MutableAttributeAccessor> attributes = component.attributes_for_write();
105 if (!attributes.has_value()) {
106 return;
107 }
108
109 Vector<std::string> unused_ids;
110 attributes->foreach_attribute([&](const bke::AttributeIter &iter) {
112 return;
113 }
114 if (keep.contains(iter.name)) {
115 return;
116 }
117 if (!attribute_filter.allow_skip(iter.name)) {
118 return;
119 }
120 unused_ids.append(iter.name);
121 });
122
123 for (const std::string &unused_id : unused_ids) {
124 attributes->remove(unused_id);
125 }
126}
127
129{
130 GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
131
132 if (!params.output_is_required("Geometry")) {
133 params.error_message_add(
135 TIP_("The attribute output cannot be used without the geometry output"));
136 params.set_default_remaining_outputs();
137 return;
138 }
139
140 const NodeGeometryAttributeCapture &storage = node_storage(params.node());
141 const AttrDomain domain = AttrDomain(storage.domain);
142
144 Vector<GField> fields;
145 Vector<std::string> attribute_id_ptrs;
146 Set<StringRef> used_attribute_ids_set;
147 for (const NodeGeometryAttributeCaptureItem &item :
148 Span{storage.capture_items, storage.capture_items_num})
149 {
150 const std::string input_identifier =
152 const std::string output_identifier =
154 std::optional<std::string> attribute_id = params.get_output_anonymous_attribute_id_if_needed(
155 output_identifier);
156 if (!attribute_id) {
157 continue;
158 }
159 used_attribute_ids_set.add(*attribute_id);
160 fields.append(params.extract_input<GField>(input_identifier));
161 attribute_id_ptrs.append(std::move(*attribute_id));
162 used_items.append(&item);
163 }
164
165 if (fields.is_empty()) {
166 params.set_output("Geometry", geometry_set);
167 params.set_default_remaining_outputs();
168 return;
169 }
170
171 Array<StringRef> attribute_ids(attribute_id_ptrs.size());
172 for (const int i : attribute_id_ptrs.index_range()) {
173 attribute_ids[i] = attribute_id_ptrs[i];
174 }
175
176 const auto capture_on = [&](GeometryComponent &component) {
177 bke::try_capture_fields_on_geometry(component, attribute_ids, domain, fields);
178 /* Changing of the anonymous attributes may require removing attributes that are no longer
179 * needed. */
181 params.get_attribute_filter("Geometry"), used_attribute_ids_set, component);
182 };
183
184 /* Run on the instances component separately to only affect the top level of instances. */
185 if (domain == AttrDomain::Instance) {
186 if (geometry_set.has_instances()) {
187 capture_on(geometry_set.get_component_for_write(GeometryComponent::Type::Instance));
188 }
189 }
190 else {
191 static const Array<GeometryComponent::Type> types = {GeometryComponent::Type::Mesh,
192 GeometryComponent::Type::PointCloud,
193 GeometryComponent::Type::Curve,
194 GeometryComponent::Type::GreasePencil};
195
196 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
197 for (const GeometryComponent::Type type : types) {
198 if (geometry_set.has(type)) {
199 capture_on(geometry_set.get_component_for_write(type));
200 }
201 }
202 });
203 }
204
205 params.set_output("Geometry", geometry_set);
206}
207
208static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
209{
211 *ntree, *node, *node, *link);
212}
213
219
220static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
221{
222 const NodeGeometryAttributeCapture &src_storage = node_storage(*src_node);
224 __func__, src_storage);
225 dst_node->storage = dst_storage;
226
228}
229
231{
232 const eNodeSocketDatatype type = eNodeSocketDatatype(params.other_socket().type);
233 if (type == SOCK_GEOMETRY) {
234 params.add_item(IFACE_("Geometry"), [](LinkSearchOpParams &params) {
235 bNode &node = params.add_node("GeometryNodeCaptureAttribute");
236 params.connect_available_socket(node, "Geometry");
237 });
238 }
240 return;
241 }
242
243 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
244 bNode &node = params.add_node("GeometryNodeCaptureAttribute");
246 node, type, params.socket.name);
247 params.update_and_connect_available_socket(node, params.socket.name);
248 });
249}
250
252 const bNode &node,
253 const bNodeSocket &output_socket)
254{
255 return &node.input_socket(output_socket.index());
256}
257
258static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
259{
261}
262
263static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
264{
266}
267
268static void node_register()
269{
270 static blender::bke::bNodeType ntype;
271 geo_node_type_base(&ntype, "GeometryNodeCaptureAttribute", GEO_NODE_CAPTURE_ATTRIBUTE);
272 ntype.ui_name = "Capture Attribute";
273 ntype.ui_description =
274 "Store the result of a field on a geometry and output the data as a node socket. Allows "
275 "remembering or interpolating data as the geometry changes, such as positions before "
276 "deformation";
277 ntype.enum_name_legacy = "CAPTURE_ATTRIBUTE";
280 ntype, "NodeGeometryAttributeCapture", node_free_storage, node_copy_storage);
281 ntype.initfunc = node_init;
282 ntype.declare = node_declare;
293}
295
296} // namespace blender::nodes::node_geo_attribute_capture_cc
297
298namespace blender::nodes {
299
300StructRNA *CaptureAttributeItemsAccessor::item_srna = &RNA_NodeGeometryCaptureAttributeItem;
301
303{
304 BLO_write_string(writer, item.name);
305}
306
311
312} // namespace blender::nodes
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define NODE_CLASS_ATTRIBUTE
Definition BKE_node.hh:448
#define GEO_NODE_CAPTURE_ATTRIBUTE
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5351
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define TIP_(msgid)
#define IFACE_(msgid)
eNodeSocketDatatype
@ SOCK_GEOMETRY
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define C
Definition RandGen.cpp:29
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
BMesh const char void * data
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
int64_t size() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
virtual std::optional< MutableAttributeAccessor > attributes_for_write()
KDTree_3d * tree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static char ** types
Definition makesdna.cc:71
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
bool attribute_name_is_anonymous(const StringRef name)
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
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)
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5603
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_blend_read(bNodeTree &, bNode &node, BlendDataReader &reader)
static void node_blend_write(const bNodeTree &, const bNode &node, BlendWriter &writer)
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 const bNodeSocket * node_internally_linked_input(const bNodeTree &, const bNode &node, const bNodeSocket &output_socket)
static void node_copy_storage(bNodeTree *, bNode *dst_node, const bNode *src_node)
static void draw_items_list_with_operators(const bContext *C, uiLayout *layout, const bNodeTree &tree, const bNode &node)
static void draw_active_item_props(const bNodeTree &tree, const bNode &node, const FunctionRef< void(PointerRNA *item_ptr)> draw_item)
void blend_write(BlendWriter *writer, const bNode &node)
void blend_read_data(BlendDataReader *reader, bNode &node)
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, std::string idname, const std::optional< int16_t > legacy_type)
NodeGeometryAttributeCaptureItem * capture_items
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:226
NodeInternallyLinkedInputFunction internally_linked_input
Definition BKE_node.hh:377
NodeBlendWriteFunction blend_write_storage_content
Definition BKE_node.hh:383
std::string ui_description
Definition BKE_node.hh:232
NodeBlendDataReadFunction blend_data_read_storage_content
Definition BKE_node.hh:384
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:249
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:347
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:371
bool(* insert_link)(bNodeTree *ntree, bNode *node, bNodeLink *link)
Definition BKE_node.hh:321
NodeDeclareFunction declare
Definition BKE_node.hh:355
void(* register_operators)()
Definition BKE_node.hh:410
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_item(BlendDataReader *reader, ItemT &item)
static std::string output_socket_identifier_for_item(const NodeGeometryAttributeCaptureItem &item)
static void blend_write_item(BlendWriter *writer, const ItemT &item)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4226