Blender V5.0
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
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
22
23#include "node_geometry_util.hh"
24
26
28
30{
31 const bNodeTree *tree = b.tree_or_null();
32 const bNode *node = b.node_or_null();
33 b.use_custom_socket_order();
34 b.allow_any_socket_order();
35
36 b.add_default_layout();
37
38 b.add_input<decl::Geometry>("Geometry")
40 "Geometry to evaluate the given fields and store the resulting attributes on. All "
41 "geometry types except volumes are supported");
42 b.add_output<decl::Geometry>("Geometry").propagate_all().align_with_previous();
43 if (node != nullptr) {
44 const NodeGeometryAttributeCapture &storage = node_storage(*node);
45 for (const NodeGeometryAttributeCaptureItem &item :
46 Span(storage.capture_items, storage.capture_items_num))
47 {
48 const eCustomDataType data_type = eCustomDataType(item.data_type);
49 const std::string input_identifier =
51 const std::string output_identifier =
53 b.add_input(data_type, item.name, input_identifier)
54 .field_on_all()
55 .socket_name_ptr(&tree->id, CaptureAttributeItemsAccessor::item_srna, &item, "name");
56 b.add_output(data_type, item.name, output_identifier).field_on_all().align_with_previous();
57 }
58 }
59 b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Field);
60 b.add_output<decl::Extend>("", "__extend__")
61 .structure_type(StructureType::Field)
62 .align_with_previous();
63}
64
65static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
66{
67 layout->use_property_split_set(true);
68 layout->use_property_decorate_set(false);
69 layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
70}
71
72static void node_init(bNodeTree * /*tree*/, bNode *node)
73{
75 data->domain = int8_t(AttrDomain::Point);
76 node->storage = data;
77}
78
80{
81 bNodeTree &tree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
82 bNode &node = *static_cast<bNode *>(ptr->data);
83
84 layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
85
86 if (uiLayout *panel = layout->panel(
87 C, "capture_attribute_items", false, IFACE_("Capture Items")))
88 {
90 C, panel, tree, node);
92 tree, node, [&](PointerRNA *item_ptr) {
93 panel->use_property_split_set(true);
94 panel->use_property_decorate_set(false);
95 panel->prop(item_ptr, "data_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
96 });
97 }
98}
99
104
105static void clean_unused_attributes(const AttributeFilter &attribute_filter,
106 const Set<StringRef> &keep,
107 GeometryComponent &component)
108{
109 std::optional<MutableAttributeAccessor> attributes = component.attributes_for_write();
110 if (!attributes.has_value()) {
111 return;
112 }
113
114 Vector<std::string> unused_ids;
115 attributes->foreach_attribute([&](const bke::AttributeIter &iter) {
117 return;
118 }
119 if (keep.contains(iter.name)) {
120 return;
121 }
122 if (!attribute_filter.allow_skip(iter.name)) {
123 return;
124 }
125 unused_ids.append(iter.name);
126 });
127
128 for (const std::string &unused_id : unused_ids) {
129 attributes->remove(unused_id);
130 }
131}
132
134{
135 GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
136
137 if (!params.output_is_required("Geometry")) {
138 params.error_message_add(
140 TIP_("The attribute output cannot be used without the geometry output"));
141 params.set_default_remaining_outputs();
142 return;
143 }
144
145 const NodeGeometryAttributeCapture &storage = node_storage(params.node());
146 const AttrDomain domain = AttrDomain(storage.domain);
147
149 Vector<GField> fields;
150 Vector<std::string> attribute_id_ptrs;
151 Set<StringRef> used_attribute_ids_set;
152 for (const NodeGeometryAttributeCaptureItem &item :
153 Span{storage.capture_items, storage.capture_items_num})
154 {
155 const std::string input_identifier =
157 const std::string output_identifier =
159 std::optional<std::string> attribute_id = params.get_output_anonymous_attribute_id_if_needed(
160 output_identifier);
161 if (!attribute_id) {
162 continue;
163 }
164 used_attribute_ids_set.add(*attribute_id);
165 fields.append(params.extract_input<GField>(input_identifier));
166 attribute_id_ptrs.append(std::move(*attribute_id));
167 used_items.append(&item);
168 }
169
170 if (fields.is_empty()) {
171 params.set_output("Geometry", geometry_set);
172 params.set_default_remaining_outputs();
173 return;
174 }
175
176 Array<StringRef> attribute_ids(attribute_id_ptrs.size());
177 for (const int i : attribute_id_ptrs.index_range()) {
178 attribute_ids[i] = attribute_id_ptrs[i];
179 }
180
181 const auto capture_on = [&](GeometryComponent &component) {
182 bke::try_capture_fields_on_geometry(component, attribute_ids, domain, fields);
183 /* Changing of the anonymous attributes may require removing attributes that are no longer
184 * needed. */
186 params.get_attribute_filter("Geometry"), used_attribute_ids_set, component);
187 };
188
189 /* Run on the instances component separately to only affect the top level of instances. */
190 if (domain == AttrDomain::Instance) {
191 if (geometry_set.has_instances()) {
192 capture_on(geometry_set.get_component_for_write(GeometryComponent::Type::Instance));
193 }
194 }
195 else {
196 static const Array<GeometryComponent::Type> types = {GeometryComponent::Type::Mesh,
197 GeometryComponent::Type::PointCloud,
198 GeometryComponent::Type::Curve,
199 GeometryComponent::Type::GreasePencil};
200
201 geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
202 for (const GeometryComponent::Type type : types) {
203 if (geometry_set.has(type)) {
204 capture_on(geometry_set.get_component_for_write(type));
205 }
206 }
207 });
208 }
209
210 params.set_output("Geometry", geometry_set);
211}
212
218
224
225static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
226{
227 const NodeGeometryAttributeCapture &src_storage = node_storage(*src_node);
229 __func__, src_storage);
230 dst_node->storage = dst_storage;
231
233}
234
236{
237 const eNodeSocketDatatype type = eNodeSocketDatatype(params.other_socket().type);
238 if (type == SOCK_GEOMETRY) {
239 params.add_item(IFACE_("Geometry"), [](LinkSearchOpParams &params) {
240 bNode &node = params.add_node("GeometryNodeCaptureAttribute");
241 params.connect_available_socket(node, "Geometry");
242 });
243 }
244 if (!CaptureAttributeItemsAccessor::supports_socket_type(type, params.node_tree().type)) {
245 return;
246 }
247
248 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
249 bNode &node = params.add_node("GeometryNodeCaptureAttribute");
251 params.node_tree, node, type, params.socket.name);
252 params.update_and_connect_available_socket(node, params.socket.name);
253 });
254}
255
257 const bNode &node,
258 const bNodeSocket &output_socket)
259{
260 return &node.input_socket(output_socket.index());
261}
262
263static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
264{
266}
267
268static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
269{
271}
272
273static void node_register()
274{
275 static blender::bke::bNodeType ntype;
276 geo_node_type_base(&ntype, "GeometryNodeCaptureAttribute", GEO_NODE_CAPTURE_ATTRIBUTE);
277 ntype.ui_name = "Capture Attribute";
278 ntype.ui_description =
279 "Store the result of a field on a geometry and output the data as a node socket. Allows "
280 "remembering or interpolating data as the geometry changes, such as positions before "
281 "deformation";
282 ntype.enum_name_legacy = "CAPTURE_ATTRIBUTE";
285 ntype, "NodeGeometryAttributeCapture", node_free_storage, node_copy_storage);
286 ntype.initfunc = node_init;
287 ntype.declare = node_declare;
298}
300
301} // namespace blender::nodes::node_geo_attribute_capture_cc
302
303namespace blender::nodes {
304
305StructRNA *CaptureAttributeItemsAccessor::item_srna = &RNA_NodeGeometryCaptureAttributeItem;
306
308{
309 BLO_write_string(writer, item.name);
310}
311
316
317} // namespace blender::nodes
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_ATTRIBUTE
Definition BKE_node.hh:462
#define GEO_NODE_CAPTURE_ATTRIBUTE
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5828
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
#define UI_ITEM_NONE
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:2416
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:5414
void foreach_real_geometry(bke::GeometrySet &geometry, FunctionRef< void(bke::GeometrySet &geometry_set)> fn)
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 bool node_insert_link(bke::NodeInsertLinkParams &params)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
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(bNodeTree &ntree, bNode &node, const eNodeSocketDatatype socket_type, const char *name, std::optional< int > dimensions=std::nullopt)
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, typename Accessor::ItemT **r_new_item=nullptr)
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
Defines a node type.
Definition BKE_node.hh:238
NodeInternallyLinkedInputFunction internally_linked_input
Definition BKE_node.hh:384
NodeBlendWriteFunction blend_write_storage_content
Definition BKE_node.hh:390
std::string ui_description
Definition BKE_node.hh:244
NodeBlendDataReadFunction blend_data_read_storage_content
Definition BKE_node.hh:391
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:261
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
bool(* insert_link)(NodeInsertLinkParams &params)
Definition BKE_node.hh:333
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:378
NodeDeclareFunction declare
Definition BKE_node.hh:362
void(* register_operators)()
Definition BKE_node.hh:417
static std::string input_socket_identifier_for_item(const NodeGeometryAttributeCaptureItem &item)
static void blend_read_data_item(BlendDataReader *reader, ItemT &item)
static bool supports_socket_type(const eNodeSocketDatatype socket_type, const int)
static std::string output_socket_identifier_for_item(const NodeGeometryAttributeCaptureItem &item)
static void blend_write_item(BlendWriter *writer, const ItemT &item)
void use_property_decorate_set(bool is_sep)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
void use_property_split_set(bool value)
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:4238