Blender V4.5
node_geo_bake.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 <fmt/format.h>
6
7#include "NOD_geo_bake.hh"
13
14#include "UI_interface.hh"
15#include "UI_resources.hh"
16
17#include "BLI_path_utils.hh"
18#include "BLI_string.h"
19
23#include "BKE_context.hh"
24#include "BKE_global.hh"
25#include "BKE_library.hh"
26#include "BKE_main.hh"
27#include "BKE_screen.hh"
28
29#include "ED_node.hh"
30
31#include "DNA_modifier_types.h"
32
33#include "RNA_access.hh"
34#include "RNA_prototypes.hh"
35
36#include "MOD_nodes.hh"
37
38#include "WM_api.hh"
39
40#include "BLO_read_write.hh"
41
42#include "node_geometry_util.hh"
43
45
46namespace bake = bke::bake;
47
49
51{
52 b.use_custom_socket_order();
53 b.allow_any_socket_order();
54
55 b.add_default_layout();
56
57 const bNodeTree *ntree = b.tree_or_null();
58 const bNode *node = b.node_or_null();
59 if (!node) {
60 return;
61 }
62 const NodeGeometryBake &storage = node_storage(*node);
63
64 for (const int i : IndexRange(storage.items_num)) {
65 const NodeGeometryBakeItem &item = storage.items[i];
66 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
67 const StringRef name = item.name;
68 const std::string identifier = BakeItemsAccessor::socket_identifier_for_item(item);
69 auto &input_decl = b.add_input(socket_type, name, identifier)
70 .socket_name_ptr(
71 &ntree->id, BakeItemsAccessor::item_srna, &item, "name");
72 auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous();
73 if (socket_type_supports_fields(socket_type)) {
74 input_decl.supports_field();
76 output_decl.field_source();
77 }
78 else {
79 output_decl.dependent_field({input_decl.index()});
80 }
81 }
82 }
83 b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
84 b.add_output<decl::Extend>("", "__extend__")
85 .structure_type(StructureType::Dynamic)
86 .align_with_previous();
87}
88
89static void node_init(bNodeTree * /*tree*/, bNode *node)
90{
92
94 data->items_num = 1;
95
96 NodeGeometryBakeItem &item = data->items[0];
97 item.name = BLI_strdup(DATA_("Geometry"));
98 item.identifier = data->next_identifier++;
99 item.attribute_domain = int16_t(AttrDomain::Point);
101
102 node->storage = data;
103}
104
110
111static void node_copy_storage(bNodeTree * /*tree*/, bNode *dst_node, const bNode *src_node)
112{
113 const NodeGeometryBake &src_storage = node_storage(*src_node);
114 auto *dst_storage = MEM_dupallocN<NodeGeometryBake>(__func__, src_storage);
115 dst_node->storage = dst_storage;
116
118}
119
120static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
121{
123 *ntree, *node, *node, *link);
124}
125
126static const CPPType &get_item_cpp_type(const eNodeSocketDatatype socket_type)
127{
128 const bke::bNodeSocketType *typeinfo = bke::node_socket_type_find_static(socket_type);
129 BLI_assert(typeinfo);
131 return *typeinfo->geometry_nodes_cpp_type;
132}
133
134static void draw_bake_items(const bContext *C, uiLayout *layout, PointerRNA node_ptr)
135{
136 bNodeTree &tree = *reinterpret_cast<bNodeTree *>(node_ptr.owner_id);
137 bNode &node = *static_cast<bNode *>(node_ptr.data);
138 NodeGeometryBake &storage = node_storage(node);
139
140 if (uiLayout *panel = layout->panel(C, "bake_items", false, IFACE_("Bake Items"))) {
143 tree, node, [&](PointerRNA *item_ptr) {
144 const NodeGeometryBakeItem &active_item = storage.items[storage.active_index];
145 uiLayoutSetPropSep(panel, true);
146 uiLayoutSetPropDecorate(panel, false);
147 panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
149 panel->prop(item_ptr, "attribute_domain", UI_ITEM_NONE, std::nullopt, ICON_NONE);
150 panel->prop(item_ptr, "is_attribute", UI_ITEM_NONE, std::nullopt, ICON_NONE);
151 }
152 });
153 }
154}
155
160
162{
164 const int items_num = bake_items.size();
165 config.domains.resize(items_num);
166 config.names.resize(items_num);
167 config.types.resize(items_num);
168 config.geometries_by_attribute.resize(items_num);
169
170 int last_geometry_index = -1;
171 for (const int item_i : bake_items.index_range()) {
172 const NodeGeometryBakeItem &item = bake_items[item_i];
173 config.types[item_i] = eNodeSocketDatatype(item.socket_type);
174 config.names[item_i] = item.name;
175 config.domains[item_i] = AttrDomain(item.attribute_domain);
176 if (item.socket_type == SOCK_GEOMETRY) {
177 last_geometry_index = item_i;
178 }
179 else if (last_geometry_index != -1) {
180 config.geometries_by_attribute[item_i].append(last_geometry_index);
181 }
182 }
183 return config;
184}
185
191 private:
192 Mutex mutex_;
194
195 public:
197 {
198 std::lock_guard lock{mutex_};
199 return map_.lookup_default(key, nullptr);
200 }
201
202 void try_add(ID &id) override
203 {
204 std::lock_guard lock{mutex_};
205 map_.add(bake::BakeDataBlockID(id), &id);
206 }
207};
208
210 const bNode &node_;
211 Span<NodeGeometryBakeItem> bake_items_;
212 bake::BakeSocketConfig bake_socket_config_;
213
214 public:
216 : node_(node)
217 {
218 debug_name_ = "Bake";
219 const NodeGeometryBake &storage = node_storage(node);
220 bake_items_ = {storage.items, storage.items_num};
221
222 MutableSpan<int> lf_index_by_bsocket = lf_graph_info.mapping.lf_index_by_bsocket;
223
224 for (const int i : bake_items_.index_range()) {
225 const NodeGeometryBakeItem &item = bake_items_[i];
226 const bNodeSocket &input_bsocket = node.input_socket(i);
227 const bNodeSocket &output_bsocket = node.output_socket(i);
229 lf_index_by_bsocket[input_bsocket.index_in_tree()] = inputs_.append_and_get_index_as(
230 item.name, type, lf::ValueUsage::Maybe);
231 lf_index_by_bsocket[output_bsocket.index_in_tree()] = outputs_.append_and_get_index_as(
232 item.name, type);
233 }
234
235 bake_socket_config_ = make_bake_socket_config(bake_items_);
236 }
237
238 void execute_impl(lf::Params &params, const lf::Context &context) const final
239 {
240 GeoNodesUserData &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
241 GeoNodesLocalUserData &local_user_data = *static_cast<GeoNodesLocalUserData *>(
242 context.local_user_data);
243 if (!user_data.call_data->self_object()) {
244 /* The self object is currently required for generating anonymous attribute names. */
246 return;
247 }
248 if (!user_data.call_data->bake_params) {
250 return;
251 }
252 std::optional<FoundNestedNodeID> found_id = find_nested_node_id(user_data, node_.identifier);
253 if (!found_id) {
255 return;
256 }
257 if (found_id->is_in_loop || found_id->is_in_closure) {
258 DummyDataBlockMap data_block_map;
259 this->pass_through(params, user_data, &data_block_map);
260 return;
261 }
262 BakeNodeBehavior *behavior = user_data.call_data->bake_params->get(found_id->id);
263 if (!behavior) {
265 return;
266 }
267 if (auto *info = std::get_if<sim_output::ReadSingle>(&behavior->behavior)) {
268 this->output_cached_state(params, user_data, behavior->data_block_map, info->state);
269 }
270 else if (auto *info = std::get_if<sim_output::ReadInterpolated>(&behavior->behavior)) {
272 behavior->data_block_map,
273 *user_data.call_data->self_object(),
274 *user_data.compute_context,
275 info->prev_state,
276 info->next_state,
277 info->mix_factor);
278 }
279 else if (std::get_if<sim_output::PassThrough>(&behavior->behavior)) {
280 this->pass_through(params, user_data, behavior->data_block_map);
281 }
282 else if (auto *info = std::get_if<sim_output::StoreNewState>(&behavior->behavior)) {
283 this->store(params, user_data, behavior->data_block_map, *info);
284 }
285 else if (auto *info = std::get_if<sim_output::ReadError>(&behavior->behavior)) {
286 if (geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(
287 user_data))
288 {
289 tree_logger->node_warnings.append(
290 *tree_logger->allocator, {node_.identifier, {NodeWarningType::Error, info->message}});
291 }
293 }
294 else {
296 }
297 }
298
303
305 GeoNodesUserData &user_data,
306 bke::bake::BakeDataBlockMap *data_block_map) const
307 {
308 std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(params,
309 data_block_map);
310 if (!bake_state) {
311 /* Wait for inputs to be computed. */
312 return;
313 }
314 Array<void *> output_values(bake_items_.size());
315 for (const int i : bake_items_.index_range()) {
316 output_values[i] = params.get_output_data_ptr(i);
317 }
318 this->move_bake_state_to_values(std::move(*bake_state),
319 data_block_map,
320 *user_data.call_data->self_object(),
321 *user_data.compute_context,
322 output_values);
323 for (const int i : bake_items_.index_range()) {
324 params.output_set(i);
325 }
326 }
327
329 GeoNodesUserData &user_data,
330 bke::bake::BakeDataBlockMap *data_block_map,
331 const sim_output::StoreNewState &info) const
332 {
333 std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(params,
334 data_block_map);
335 if (!bake_state) {
336 /* Wait for inputs to be computed. */
337 return;
338 }
339 this->output_cached_state(params, user_data, data_block_map, *bake_state);
340 info.store_fn(std::move(*bake_state));
341 }
342
344 GeoNodesUserData &user_data,
345 bke::bake::BakeDataBlockMap *data_block_map,
346 const bake::BakeStateRef &bake_state) const
347 {
348 Array<void *> output_values(bake_items_.size());
349 for (const int i : bake_items_.index_range()) {
350 output_values[i] = params.get_output_data_ptr(i);
351 }
352 this->copy_bake_state_to_values(bake_state,
353 data_block_map,
354 *user_data.call_data->self_object(),
355 *user_data.compute_context,
356 output_values);
357 for (const int i : bake_items_.index_range()) {
358 params.output_set(i);
359 }
360 }
361
363 bke::bake::BakeDataBlockMap *data_block_map,
364 const Object &self_object,
365 const ComputeContext &compute_context,
366 const bake::BakeStateRef &prev_state,
368 const float mix_factor) const
369 {
370 Array<void *> output_values(bake_items_.size());
371 for (const int i : bake_items_.index_range()) {
372 output_values[i] = params.get_output_data_ptr(i);
373 }
375 prev_state, data_block_map, self_object, compute_context, output_values);
376
377 Array<void *> next_values(bake_items_.size());
378 LinearAllocator<> allocator;
379 for (const int i : bake_items_.index_range()) {
380 const CPPType &type = *outputs_[i].type;
381 next_values[i] = allocator.allocate(type);
382 }
384 next_state, data_block_map, self_object, compute_context, next_values);
385
386 for (const int i : bake_items_.index_range()) {
387 mix_baked_data_item(eNodeSocketDatatype(bake_items_[i].socket_type),
388 output_values[i],
389 next_values[i],
390 mix_factor);
391 }
392
393 for (const int i : bake_items_.index_range()) {
394 const CPPType &type = *outputs_[i].type;
395 type.destruct(next_values[i]);
396 }
397
398 for (const int i : bake_items_.index_range()) {
399 params.output_set(i);
400 }
401 }
402
403 std::optional<bake::BakeState> get_bake_state_from_inputs(
404 lf::Params &params, bke::bake::BakeDataBlockMap *data_block_map) const
405 {
406 Array<void *> input_values(bake_items_.size());
407 for (const int i : bake_items_.index_range()) {
408 input_values[i] = params.try_get_input_data_ptr_or_request(i);
409 }
410 if (input_values.as_span().contains(nullptr)) {
411 /* Wait for inputs to be computed. */
412 return std::nullopt;
413 }
414
416 input_values, bake_socket_config_, data_block_map);
417
418 bake::BakeState bake_state;
419 for (const int i : bake_items_.index_range()) {
420 const NodeGeometryBakeItem &item = bake_items_[i];
421 std::unique_ptr<bake::BakeItem> &bake_item = bake_items[i];
422 if (bake_item) {
423 bake_state.items_by_id.add_new(item.identifier, std::move(bake_item));
424 }
425 }
426 return bake_state;
427 }
428
430 bke::bake::BakeDataBlockMap *data_block_map,
431 const Object &self_object,
432 const ComputeContext &compute_context,
433 Span<void *> r_output_values) const
434 {
435 Vector<bake::BakeItem *> bake_items;
436 for (const NodeGeometryBakeItem &item : bake_items_) {
437 std::unique_ptr<bake::BakeItem> *bake_item = bake_state.items_by_id.lookup_ptr(
438 item.identifier);
439 bake_items.append(bake_item ? bake_item->get() : nullptr);
440 }
442 bake_items,
443 bake_socket_config_,
444 data_block_map,
445 [&](const int i, const CPPType &type) {
446 return this->make_attribute_field(self_object, compute_context, bake_items_[i], type);
447 },
448 r_output_values);
449 }
450
452 bke::bake::BakeDataBlockMap *data_block_map,
453 const Object &self_object,
454 const ComputeContext &compute_context,
455 Span<void *> r_output_values) const
456 {
458 for (const NodeGeometryBakeItem &item : bake_items_) {
459 const bake::BakeItem *const *bake_item = bake_state.items_by_id.lookup_ptr(item.identifier);
460 bake_items.append(bake_item ? *bake_item : nullptr);
461 }
463 bake_items,
464 bake_socket_config_,
465 data_block_map,
466 [&](const int i, const CPPType &type) {
467 return this->make_attribute_field(self_object, compute_context, bake_items_[i], type);
468 },
469 r_output_values);
470 }
471
472 std::shared_ptr<AttributeFieldInput> make_attribute_field(const Object &self_object,
473 const ComputeContext &compute_context,
474 const NodeGeometryBakeItem &item,
475 const CPPType &type) const
476 {
477 std::string attribute_name = bke::hash_to_anonymous_attribute_name(
478 compute_context.hash(), self_object.id.name, node_.identifier, item.identifier);
479 std::string socket_inspection_name = make_anonymous_attribute_socket_inspection_string(
480 node_.label_or_name(), item.name);
481 return std::make_shared<AttributeFieldInput>(
482 std::move(attribute_name), type, std::move(socket_inspection_name));
483 }
484};
485
487{
488 BakeDrawContext ctx;
489 if (!get_bake_draw_context(&params.C, params.node, ctx)) {
490 return;
491 }
494 row.text = TIP_("Can't bake in zone");
495 row.icon = ICON_ERROR;
496 params.rows.append(std::move(row));
497 }
498 if (ctx.is_baked) {
500 row.text = get_baked_string(ctx);
501 params.rows.append(std::move(row));
502 }
503}
504
505static void node_layout(uiLayout *layout, bContext *C, PointerRNA *ptr)
506{
507 BakeDrawContext ctx;
508 const bNode &node = *static_cast<const bNode *>(ptr->data);
509 if (!get_bake_draw_context(C, node, ctx)) {
510 return;
511 }
514 uiLayout *col = &layout->column(false);
515 {
516 uiLayout *row = &col->row(true);
517 uiLayoutSetEnabled(row, !ctx.is_baked);
518 row->prop(&ctx.bake_rna, "bake_mode", UI_ITEM_R_EXPAND, IFACE_("Mode"), ICON_NONE);
519 }
521}
522
524{
525 draw_bake_items(C, layout, *ptr);
526
527 BakeDrawContext ctx;
528 const bNode &node = *static_cast<const bNode *>(ptr->data);
529 if (!get_bake_draw_context(C, node, ctx)) {
530 return;
531 }
532
535
536 {
537 uiLayout *col = &layout->column(false);
538 {
539 uiLayout *row = &col->row(true);
540 uiLayoutSetEnabled(row, !ctx.is_baked);
541 row->prop(&ctx.bake_rna, "bake_mode", UI_ITEM_R_EXPAND, IFACE_("Mode"), ICON_NONE);
542 }
543
544 draw_bake_button_row(ctx, col, true);
545 if (const std::optional<std::string> bake_state_str = get_bake_state_string(ctx)) {
546 uiLayout *row = &col->row(true);
547 row->label(*bake_state_str, ICON_NONE);
548 }
549 }
550
551 draw_common_bake_settings(C, ctx, layout);
552 draw_data_blocks(C, layout, ctx.bake_rna);
553}
554
556{
557 const eNodeSocketDatatype type = eNodeSocketDatatype(params.other_socket().type);
558 if (type == SOCK_GEOMETRY) {
559 params.add_item(IFACE_("Geometry"), [](LinkSearchOpParams &params) {
560 bNode &node = params.add_node("GeometryNodeBake");
561 params.connect_available_socket(node, "Geometry");
562 });
563 return;
564 }
566 return;
567 }
568
569 params.add_item(
570 IFACE_("Value"),
571 [type](LinkSearchOpParams &params) {
572 bNode &node = params.add_node("GeometryNodeBake");
574 node, type, params.socket.name);
575 params.update_and_connect_available_socket(node, params.socket.name);
576 },
577 -1);
578}
579
581 const bNode &node,
582 const bNodeSocket &output_socket)
583{
584 /* Internal links should always map corresponding input and output sockets. */
585 return &node.input_by_identifier(output_socket.identifier);
586}
587
588static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
589{
591}
592
593static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
594{
596}
597
598static void node_register()
599{
600 static blender::bke::bNodeType ntype;
601 geo_node_type_base(&ntype, "GeometryNodeBake", GEO_NODE_BAKE);
602 ntype.ui_name = "Bake";
603 ntype.ui_description = "Cache the incoming data so that it can be used without recomputation";
604 ntype.enum_name_legacy = "BAKE";
606 ntype.declare = node_declare;
608 ntype.initfunc = node_init;
619}
620NOD_REGISTER_NODE(node_register)
621
622} // namespace blender::nodes::node_geo_bake_cc
623
624namespace blender::nodes {
625
626bool get_bake_draw_context(const bContext *C, const bNode &node, BakeDrawContext &r_ctx)
627{
629 r_ctx.node = &node;
630 r_ctx.snode = CTX_wm_space_node(C);
631 if (!r_ctx.snode) {
632 return false;
633 }
634 std::optional<ed::space_node::ObjectAndModifier> object_and_modifier =
636 if (!object_and_modifier) {
637 return false;
638 }
639 r_ctx.object = object_and_modifier->object;
640 r_ctx.nmd = object_and_modifier->nmd;
641 const std::optional<FoundNestedNodeID> bake_id = ed::space_node::find_nested_node_id_in_root(
642 *r_ctx.snode, *r_ctx.node);
643 if (!bake_id) {
644 return false;
645 }
646 r_ctx.is_bakeable_in_current_context = !bake_id->is_in_loop && !bake_id->is_in_closure;
647 r_ctx.bake = nullptr;
648 for (const NodesModifierBake &iter_bake : Span(r_ctx.nmd->bakes, r_ctx.nmd->bakes_num)) {
649 if (iter_bake.id == bake_id->id) {
650 r_ctx.bake = &iter_bake;
651 break;
652 }
653 }
654 if (!r_ctx.bake) {
655 return false;
656 }
657
659 const_cast<ID *>(&r_ctx.object->id), &RNA_NodesModifierBake, (void *)r_ctx.bake);
660 if (r_ctx.nmd->runtime->cache) {
661 const bke::bake::ModifierCache &cache = *r_ctx.nmd->runtime->cache;
662 std::lock_guard lock{cache.mutex};
663 if (const std::unique_ptr<bke::bake::BakeNodeCache> *node_cache_ptr =
664 cache.bake_cache_by_id.lookup_ptr(bake_id->id))
665 {
666 const bke::bake::BakeNodeCache &node_cache = **node_cache_ptr;
667 if (!node_cache.bake.frames.is_empty()) {
668 const int first_frame = node_cache.bake.frames.first()->frame.frame();
669 const int last_frame = node_cache.bake.frames.last()->frame.frame();
670 r_ctx.baked_range = IndexRange(first_frame, last_frame - first_frame + 1);
671 }
672 }
673 else if (const std::unique_ptr<bke::bake::SimulationNodeCache> *node_cache_ptr =
674 cache.simulation_cache_by_id.lookup_ptr(bake_id->id))
675 {
676 const bke::bake::SimulationNodeCache &node_cache = **node_cache_ptr;
677 if (!node_cache.bake.frames.is_empty() &&
679 {
680 const int first_frame = node_cache.bake.frames.first()->frame.frame();
681 const int last_frame = node_cache.bake.frames.last()->frame.frame();
682 r_ctx.baked_range = IndexRange(first_frame, last_frame - first_frame + 1);
683 }
684 }
685 }
686 const Scene *scene = CTX_data_scene(C);
688 *scene, *r_ctx.object, *r_ctx.nmd, r_ctx.bake->id);
689 r_ctx.bake_still = node.type_legacy == GEO_NODE_BAKE &&
691 r_ctx.is_baked = r_ctx.baked_range.has_value();
692 r_ctx.bake_target = bke::bake::get_node_bake_target(*r_ctx.object, *r_ctx.nmd, r_ctx.bake->id);
693
694 return true;
695}
696
697std::string get_baked_string(const BakeDrawContext &ctx)
698{
699 if (ctx.bake_still && ctx.baked_range->size() == 1) {
700 return fmt::format(fmt::runtime(RPT_("Baked Frame {}")), ctx.baked_range->first());
701 }
702 return fmt::format(
703 fmt::runtime(RPT_("Baked {} - {}")), ctx.baked_range->first(), ctx.baked_range->last());
704}
705
706std::optional<std::string> get_bake_state_string(const BakeDrawContext &ctx)
707{
708 if (G.is_rendering) {
709 /* Avoid accessing data that is generated while baking. */
710 return std::nullopt;
711 }
712 if (ctx.is_baked) {
713 const std::string baked_str = get_baked_string(ctx);
715 BLI_str_format_byte_unit(size_str, ctx.bake->bake_size, true);
716 if (ctx.bake->packed) {
717 return fmt::format(fmt::runtime(RPT_("{} ({} packed)")), baked_str, size_str);
718 }
719 return fmt::format(fmt::runtime(RPT_("{} ({} on disk)")), baked_str, size_str);
720 }
721 if (ctx.frame_range.has_value()) {
722 if (!ctx.bake_still) {
723 return fmt::format(
724 fmt::runtime(RPT_("Frames {} - {}")), ctx.frame_range->first(), ctx.frame_range->last());
725 }
726 }
727 return std::nullopt;
728}
729
730void draw_bake_button_row(const BakeDrawContext &ctx, uiLayout *layout, const bool is_in_sidebar)
731{
732 uiLayout *col = &layout->column(true);
733 uiLayout *row = &col->row(true);
734 {
735 const char *bake_label = IFACE_("Bake");
736 if (is_in_sidebar) {
737 bake_label = ctx.bake_target == NODES_MODIFIER_BAKE_TARGET_DISK ? IFACE_("Bake to Disk") :
738 IFACE_("Bake Packed");
739 }
740
741 PointerRNA ptr = row->op("OBJECT_OT_geometry_node_bake_single",
742 bake_label,
743 ICON_NONE,
747 RNA_string_set(&ptr, "modifier_name", ctx.nmd->modifier.name);
748 RNA_int_set(&ptr, "bake_id", ctx.bake->id);
749 }
750 {
751 uiLayout *subrow = &row->row(true);
752 uiLayoutSetActive(subrow, ctx.is_baked);
753 if (is_in_sidebar) {
754 if (ctx.is_baked && !G.is_rendering) {
755 if (ctx.bake->packed) {
756 PointerRNA ptr = subrow->op("OBJECT_OT_geometry_node_bake_unpack_single",
757 "",
758 ICON_PACKAGE,
762 RNA_string_set(&ptr, "modifier_name", ctx.nmd->modifier.name);
763 RNA_int_set(&ptr, "bake_id", ctx.bake->id);
764 }
765 else {
766 PointerRNA ptr = subrow->op("OBJECT_OT_geometry_node_bake_pack_single",
767 "",
768 ICON_UGLYPACKAGE,
772 RNA_string_set(&ptr, "modifier_name", ctx.nmd->modifier.name);
773 RNA_int_set(&ptr, "bake_id", ctx.bake->id);
774 }
775 }
776 else {
777 /* If the data is not yet baked, still show the icon based on the derived bake target. */
778 const int icon = ctx.bake_target == NODES_MODIFIER_BAKE_TARGET_DISK ? ICON_UGLYPACKAGE :
779 ICON_PACKAGE;
780 PointerRNA ptr = subrow->op("OBJECT_OT_geometry_node_bake_pack_single",
781 "",
782 icon,
785 }
786 }
787 {
788 PointerRNA ptr = subrow->op("OBJECT_OT_geometry_node_bake_delete_single",
789 "",
790 ICON_TRASH,
794 RNA_string_set(&ptr, "modifier_name", ctx.nmd->modifier.name);
795 RNA_int_set(&ptr, "bake_id", ctx.bake->id);
796 }
797 }
798}
799
801{
802 uiLayoutSetPropSep(layout, true);
803 uiLayoutSetPropDecorate(layout, false);
804
805 uiLayout *settings_col = &layout->column(false);
806 uiLayoutSetActive(settings_col, !ctx.is_baked);
807 {
808 uiLayout *col = &settings_col->column(true);
809 col->prop(&ctx.bake_rna, "bake_target", UI_ITEM_NONE, std::nullopt, ICON_NONE);
810 uiLayout *subcol = &col->column(true);
812 subcol->prop(&ctx.bake_rna, "use_custom_path", UI_ITEM_NONE, IFACE_("Custom Path"), ICON_NONE);
813 uiLayout *subsubcol = &subcol->column(true);
814 const bool use_custom_path = ctx.bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH;
815 uiLayoutSetActive(subsubcol, use_custom_path);
816 Main *bmain = CTX_data_main(C);
817 auto bake_path = bke::bake::get_node_bake_path(*bmain, *ctx.object, *ctx.nmd, ctx.bake->id);
818
819 char placeholder_path[FILE_MAX] = "";
820 if (StringRef(ctx.bake->directory).is_empty() &&
821 !(ctx.bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH) && bake_path.has_value() &&
822 bake_path->bake_dir.has_value())
823 {
824 STRNCPY(placeholder_path, bake_path->bake_dir->c_str());
826 BLI_path_rel(placeholder_path, BKE_main_blendfile_path(bmain));
827 }
828 }
829
830 subsubcol->prop(&ctx.bake_rna,
831 RNA_struct_find_property(&ctx.bake_rna, "directory"),
832 -1,
833 0,
835 IFACE_("Path"),
836 ICON_NONE,
837 placeholder_path);
838 }
839 {
840 uiLayout *col = &settings_col->column(true);
841 col->prop(&ctx.bake_rna,
842 "use_custom_simulation_frame_range",
844 IFACE_("Custom Range"),
845 ICON_NONE);
846 uiLayout *subcol = &col->column(true);
848 subcol->prop(&ctx.bake_rna, "frame_start", UI_ITEM_NONE, IFACE_("Start"), ICON_NONE);
849 subcol->prop(&ctx.bake_rna, "frame_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
850 }
851}
852
853static void draw_bake_data_block_list_item(uiList * /*ui_list*/,
854 const bContext * /*C*/,
855 uiLayout *layout,
856 PointerRNA * /*idataptr*/,
857 PointerRNA *itemptr,
858 int /*icon*/,
859 PointerRNA * /*active_dataptr*/,
860 const char * /*active_propname*/,
861 int /*index*/,
862 int /*flt_flag*/)
863{
864 auto &data_block = *static_cast<NodesModifierDataBlock *>(itemptr->data);
865 uiLayout *row = &layout->row(true);
866
867 std::string name;
868 if (StringRef(data_block.lib_name).is_empty()) {
869 name = data_block.id_name;
870 }
871 else {
872 name = fmt::format("{} [{}]", data_block.id_name, data_block.lib_name);
873 }
874
875 row->prop(itemptr, "id", UI_ITEM_NONE, name, ICON_NONE);
876}
877
878void draw_data_blocks(const bContext *C, uiLayout *layout, PointerRNA &bake_rna)
879{
880 static const uiListType *data_block_list = []() {
881 uiListType *list = MEM_callocN<uiListType>(__func__);
882 STRNCPY(list->idname, "DATA_UL_nodes_modifier_data_blocks");
884 WM_uilisttype_add(list);
885 return list;
886 }();
887
888 PointerRNA data_blocks_ptr = RNA_pointer_create_discrete(
889 bake_rna.owner_id, &RNA_NodesModifierBakeDataBlocks, bake_rna.data);
890
891 if (uiLayout *panel = layout->panel(
892 C, "data_block_references", true, IFACE_("Data-Block References")))
893 {
894 uiTemplateList(panel,
895 C,
896 data_block_list->idname,
897 "",
898 &bake_rna,
899 "data_blocks",
900 &data_blocks_ptr,
901 "active_index",
902 nullptr,
903 3,
904 5,
906 0,
908 }
909}
910
911std::unique_ptr<LazyFunction> get_bake_lazy_function(
912 const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
913{
914 namespace file_ns = blender::nodes::node_geo_bake_cc;
916 return std::make_unique<file_ns::LazyFunctionForBakeNode>(node, lf_graph_info);
917}
918
919StructRNA *BakeItemsAccessor::item_srna = &RNA_NodeGeometryBakeItem;
920
922{
923 BLO_write_string(writer, item.name);
924}
925
927{
928 BLO_read_string(reader, &item.name);
929}
930
931}; // namespace blender::nodes
SpaceNode * CTX_wm_space_node(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:877
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:447
#define GEO_NODE_SIMULATION_OUTPUT
#define GEO_NODE_BAKE
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define final(a, b, c)
Definition BLI_hash.h:19
#define FILE_MAX
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
#define BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE
Definition BLI_string.h:28
void BLI_str_format_byte_unit(char dst[BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE], long long int bytes, bool base_10) ATTR_NONNULL(1)
Definition string.cc:1205
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define ELEM(...)
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5351
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define RPT_(msgid)
#define TIP_(msgid)
#define IFACE_(msgid)
#define DATA_(msgid)
@ NODES_MODIFIER_BAKE_TARGET_DISK
@ NODES_MODIFIER_BAKE_CUSTOM_PATH
@ NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE
@ NODES_MODIFIER_BAKE_MODE_STILL
@ GEO_NODE_BAKE_ITEM_IS_ATTRIBUTE
eNodeSocketDatatype
@ SOCK_GEOMETRY
@ UILST_LAYOUT_DEFAULT
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define C
Definition RandGen.cpp:29
void uiTemplateList(uiLayout *layout, const bContext *C, const char *listtype_name, const char *list_id, PointerRNA *dataptr, blender::StringRefNull 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)
@ UI_TEMPLATE_LIST_FLAG_NONE
@ UI_ITEM_R_EXPAND
void uiLayoutSetActive(uiLayout *layout, bool active)
void uiLayoutSetEnabled(uiLayout *layout, bool enabled)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
volatile int lock
BMesh const char void * data
void resize(const int64_t new_size)
Span< T > as_span() const
Definition BLI_array.hh:232
void destruct(void *ptr) const
const ComputeContextHash & hash() const
void * allocate(const int64_t size, const int64_t alignment)
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
void append(const T &value)
void resize(const int64_t new_size)
virtual BakeNodeBehavior * get(const int id) const =0
void output_mixed_cached_state(lf::Params &params, bke::bake::BakeDataBlockMap *data_block_map, const Object &self_object, const ComputeContext &compute_context, const bake::BakeStateRef &prev_state, const bake::BakeStateRef &next_state, const float mix_factor) const
void copy_bake_state_to_values(const bake::BakeStateRef &bake_state, bke::bake::BakeDataBlockMap *data_block_map, const Object &self_object, const ComputeContext &compute_context, Span< void * > r_output_values) const
LazyFunctionForBakeNode(const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
void store(lf::Params &params, GeoNodesUserData &user_data, bke::bake::BakeDataBlockMap *data_block_map, const sim_output::StoreNewState &info) const
void output_cached_state(lf::Params &params, GeoNodesUserData &user_data, bke::bake::BakeDataBlockMap *data_block_map, const bake::BakeStateRef &bake_state) const
void move_bake_state_to_values(bake::BakeState bake_state, bke::bake::BakeDataBlockMap *data_block_map, const Object &self_object, const ComputeContext &compute_context, Span< void * > r_output_values) const
std::shared_ptr< AttributeFieldInput > make_attribute_field(const Object &self_object, const ComputeContext &compute_context, const NodeGeometryBakeItem &item, const CPPType &type) const
void pass_through(lf::Params &params, GeoNodesUserData &user_data, bke::bake::BakeDataBlockMap *data_block_map) const
void execute_impl(lf::Params &params, const lf::Context &context) const final
std::optional< bake::BakeState > get_bake_state_from_inputs(lf::Params &params, bke::bake::BakeDataBlockMap *data_block_map) const
KDTree_3d * tree
uint col
#define ID_IS_EDITABLE(_id)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
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
static void next_state()
#define G(x, y, z)
std::optional< NodesModifierBakeTarget > get_node_bake_target(const Object &object, const NodesModifierData &nmd, int node_id)
Array< std::unique_ptr< BakeItem > > move_socket_values_to_bake_items(Span< void * > socket_values, const BakeSocketConfig &config, BakeDataBlockMap *data_block_map)
std::optional< BakePath > get_node_bake_path(const Main &bmain, const Object &object, const NodesModifierData &nmd, int node_id)
std::optional< IndexRange > get_node_bake_frame_range(const Scene &scene, const Object &object, const NodesModifierData &nmd, int node_id)
void copy_bake_items_to_socket_values(Span< const BakeItem * > bake_items, const BakeSocketConfig &config, BakeDataBlockMap *data_block_map, FunctionRef< std::shared_ptr< AttributeFieldInput >(int, const CPPType &)> make_attribute_field, Span< void * > r_socket_values)
void move_bake_items_to_socket_values(Span< BakeItem * > bake_items, const BakeSocketConfig &config, BakeDataBlockMap *data_block_map, FunctionRef< std::shared_ptr< AttributeFieldInput >(int socket_index, const CPPType &)> make_attribute_field, Span< void * > r_socket_values)
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
bNodeSocketType * node_socket_type_find_static(int type, int subtype=0)
Definition node.cc:2803
std::string hash_to_anonymous_attribute_name(Args &&...args)
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
std::optional< nodes::FoundNestedNodeID > find_nested_node_id_in_root(const SpaceNode &snode, const bNode &node)
std::optional< ObjectAndModifier > get_modifier_for_node_editor(const SpaceNode &snode)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void node_layout(uiLayout *layout, bContext *C, PointerRNA *ptr)
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
static void draw_bake_items(const bContext *C, uiLayout *layout, PointerRNA node_ptr)
static const CPPType & get_item_cpp_type(const eNodeSocketDatatype socket_type)
static bake::BakeSocketConfig make_bake_socket_config(const Span< NodeGeometryBakeItem > bake_items)
static void node_blend_write(const bNodeTree &, const bNode &node, BlendWriter &writer)
static void node_extra_info(NodeExtraInfoParams &params)
static void node_free_storage(bNode *node)
static void node_blend_read(bNodeTree &, bNode &node, BlendDataReader &reader)
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 node_declare(NodeDeclarationBuilder &b)
static void node_init(bNodeTree *, bNode *node)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
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)
bool get_bake_draw_context(const bContext *C, const bNode &node, BakeDrawContext &r_ctx)
void draw_data_blocks(const bContext *C, uiLayout *layout, PointerRNA &bake_rna)
static void draw_bake_data_block_list_item(uiList *, const bContext *, uiLayout *layout, PointerRNA *, PointerRNA *itemptr, int, PointerRNA *, const char *, int, int)
void draw_common_bake_settings(bContext *C, BakeDrawContext &ctx, uiLayout *layout)
void draw_bake_button_row(const BakeDrawContext &ctx, uiLayout *layout, bool is_in_sidebar=false)
std::string make_anonymous_attribute_socket_inspection_string(const bNodeSocket &socket)
std::unique_ptr< LazyFunction > get_bake_lazy_function(const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
std::optional< FoundNestedNodeID > find_nested_node_id(const GeoNodesUserData &user_data, const int node_id)
bool socket_type_supports_fields(const eNodeSocketDatatype socket_type)
void set_default_remaining_node_outputs(lf::Params &params, const bNode &node)
std::optional< std::string > get_bake_state_string(const BakeDrawContext &ctx)
void mix_baked_data_item(eNodeSocketDatatype socket_type, void *prev, const void *next, const float factor)
std::string get_baked_string(const BakeDrawContext &ctx)
std::mutex Mutex
Definition BLI_mutex.hh:47
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
NodeGeometryBakeItem * items
NodesModifierPackedBake * packed
NodesModifierRuntimeHandle * runtime
NodesModifierBake * bakes
ID * owner_id
Definition RNA_types.hh:51
void * data
Definition RNA_types.hh:53
char identifier[64]
int16_t type_legacy
void * storage
Defines a socket type.
Definition BKE_node.hh:152
const blender::CPPType * geometry_nodes_cpp_type
Definition BKE_node.hh:203
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
NodeExtraInfoFunction get_extra_info
Definition BKE_node.hh:374
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
Vector< Vector< int, 1 > > geometries_by_attribute
Map< int, const BakeItem * > items_by_id
Map< int, std::unique_ptr< BakeItem > > items_by_id
Map< int, std::unique_ptr< SimulationNodeCache > > simulation_cache_by_id
Map< int, std::unique_ptr< BakeNodeCache > > bake_cache_by_id
Vector< std::unique_ptr< FrameCache > > frames
std::optional< NodesModifierBakeTarget > bake_target
std::optional< IndexRange > frame_range
const NodesModifierData * nmd
std::optional< IndexRange > baked_range
const NodesModifierBake * bake
static void blend_write_item(BlendWriter *writer, const ItemT &item)
static std::string socket_identifier_for_item(const NodeGeometryBakeItem &item)
static bool supports_socket_type(const eNodeSocketDatatype socket_type)
static void blend_read_data_item(BlendDataReader *reader, ItemT &item)
geo_eval_log::GeoTreeLogger * try_get_tree_logger(const GeoNodesUserData &user_data) const
ID * lookup_or_remember_missing(const bake::BakeDataBlockID &key) override
std::function< void(bke::bake::BakeState state)> store_fn
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
uiLayout & row(bool align)
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)
char idname[BKE_ST_MAXNAME]
uiListDrawItemFunc draw_item
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4227
void WM_operator_properties_id_lookup_set_from_id(PointerRNA *ptr, const ID *id)
bool WM_uilisttype_add(uiListType *ult)