Blender V5.0
object_bake_simulation.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 <sstream>
6
7#include "BLI_fileops.hh"
8#include "BLI_listbase.h"
9#include "BLI_path_utils.hh"
10#include "BLI_string.h"
11#include "BLI_vector.hh"
12
13#include "BLT_translation.hh"
14
15#include "WM_api.hh"
16#include "WM_types.hh"
17
18#include "ED_object.hh"
19#include "ED_screen.hh"
20
21#include "DNA_array_utils.hh"
22#include "DNA_modifier_types.h"
24
27#include "BKE_context.hh"
28#include "BKE_global.hh"
29#include "BKE_lib_id.hh"
30#include "BKE_library.hh"
31#include "BKE_main.hh"
32#include "BKE_modifier.hh"
34#include "BKE_node_runtime.hh"
35#include "BKE_packedFile.hh"
36#include "BKE_report.hh"
37#include "BKE_scene.hh"
38
39#include "RNA_access.hh"
40#include "RNA_define.hh"
41
42#include "DEG_depsgraph.hh"
43
44#include "MOD_nodes.hh"
45
46#include "object_intern.hh"
47
48#include "UI_interface.hh"
50
51namespace bake = blender::bke::bake;
52
54
56{
58 return false;
59 }
60 return true;
61}
62
72
73static void simulate_to_frame_startjob(void *customdata, wmJobWorkerStatus *worker_status)
74{
75 SimulateToFrameJob &job = *static_cast<SimulateToFrameJob *>(customdata);
76 G.is_rendering = true;
77 G.is_break = false;
78 WM_locked_interface_set(job.wm, true);
79
80 Vector<Object *> objects_to_calc;
81 for (Object *object : job.objects) {
82 if (!BKE_id_is_editable(job.bmain, &object->id)) {
83 continue;
84 }
85 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
86 if (md->type != eModifierType_Nodes) {
87 continue;
88 }
89 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
90 if (!nmd->runtime->cache) {
91 continue;
92 }
93 for (auto item : nmd->runtime->cache->simulation_cache_by_id.items()) {
94 if (item.value->cache_status != bake::CacheStatus::Baked) {
95 item.value->reset();
96 }
97 }
98 }
99 objects_to_calc.append(object);
100 }
101
102 worker_status->progress = 0.0f;
103 worker_status->do_update = true;
104
105 const float frame_step_size = 1.0f;
106 const float progress_per_frame = 1.0f /
107 (float(job.end_frame - job.start_frame + 1) / frame_step_size);
108 const int old_frame = job.scene->r.cfra;
109
110 for (float frame_f = job.start_frame; frame_f <= job.end_frame; frame_f += frame_step_size) {
111 const SubFrame frame{frame_f};
112
113 if (G.is_break || worker_status->stop) {
114 break;
115 }
116
117 job.scene->r.cfra = frame.frame();
118 job.scene->r.subframe = frame.subframe();
119
121
122 worker_status->progress += progress_per_frame;
123 worker_status->do_update = true;
124 }
125
126 job.scene->r.cfra = old_frame;
128
129 worker_status->progress = 1.0f;
130 worker_status->do_update = true;
131}
132
133static void simulate_to_frame_endjob(void *customdata)
134{
135 SimulateToFrameJob &job = *static_cast<SimulateToFrameJob *>(customdata);
136 WM_locked_interface_set(job.wm, false);
137 G.is_rendering = false;
139}
140
142 wmOperator *op,
143 const wmEvent * /*event*/)
144{
146 Scene *scene = CTX_data_scene(C);
148 Main *bmain = CTX_data_main(C);
149
150 SimulateToFrameJob *job = MEM_new<SimulateToFrameJob>(__func__);
151 job->wm = wm;
152 job->bmain = bmain;
153 job->depsgraph = depsgraph;
154 job->scene = scene;
155 job->start_frame = scene->r.sfra;
156 job->end_frame = scene->r.cfra;
157
158 if (RNA_boolean_get(op->ptr, "selected")) {
159 CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
160 job->objects.append(object);
161 }
163 }
164 else {
165 if (Object *object = CTX_data_active_object(C)) {
166 job->objects.append(object);
167 }
168 }
169
170 wmJob *wm_job = WM_jobs_get(wm,
173 "Calculating simulation...",
176
178 wm_job, job, [](void *job) { MEM_delete(static_cast<SimulateToFrameJob *>(job)); });
181 wm_job, simulate_to_frame_startjob, nullptr, nullptr, simulate_to_frame_endjob);
182
186}
187
198
200{
202 return false;
203 }
205 const bool use_frame_cache = ob->flag & OB_FLAG_USE_SIMULATION_CACHE;
206 if (!use_frame_cache) {
207 CTX_wm_operator_poll_msg_set(C, "Cache has to be enabled");
208 return false;
209 }
210 return true;
211}
212
218
220 std::optional<bake::BakePath> path;
223 std::unique_ptr<bake::BlobWriteSharing> blob_sharing;
224};
225
235
236static void try_delete_bake(
237 Main *bmain, Object &object, NodesModifierData &nmd, const int bake_id, ReportList *reports);
238static void reset_old_bake_cache(NodeBakeRequest &request);
239
241{
242 for (NodeBakeRequest &request : job.bake_requests) {
243 request.nmd->runtime->cache->requested_bakes.add(request.bake_id);
244 /* Using #DEG_id_tag_update would tag this as user-modified which is not the case here and has
245 * the issue that it invalidates simulation caches. */
247 job.depsgraph, &request.object->id, ID_RECALC_GEOMETRY);
248 }
249}
250
252{
253 for (NodeBakeRequest &request : job.bake_requests) {
254 request.nmd->runtime->cache->requested_bakes.clear();
255 }
256}
257
258static void bake_geometry_nodes_startjob(void *customdata, wmJobWorkerStatus *worker_status)
259{
260 BakeGeometryNodesJob &job = *static_cast<BakeGeometryNodesJob *>(customdata);
261 G.is_rendering = true;
262 G.is_break = false;
263
264 int global_bake_start_frame = INT32_MAX;
265 int global_bake_end_frame = INT32_MIN;
266
267 for (NodeBakeRequest &request : job.bake_requests) {
268 global_bake_start_frame = std::min(global_bake_start_frame, request.frame_start);
269 global_bake_end_frame = std::max(global_bake_end_frame, request.frame_end);
270 }
271
272 worker_status->progress = 0.0f;
273 worker_status->do_update = true;
274
275 const int frames_to_bake = global_bake_end_frame - global_bake_start_frame + 1;
276
277 const float frame_step_size = 1.0f;
278 const float progress_per_frame = frame_step_size / frames_to_bake;
279 const int old_frame = job.scene->r.cfra;
280
281 struct MemoryBakeFile {
282 std::string name;
283 std::string data;
284 };
285
286 struct PackedBake {
287 Vector<MemoryBakeFile> meta_files;
288 Vector<MemoryBakeFile> blob_files;
289 };
290
291 Map<NodeBakeRequest *, PackedBake> packed_data_by_bake;
293
294 for (float frame_f = global_bake_start_frame; frame_f <= global_bake_end_frame;
295 frame_f += frame_step_size)
296 {
297 const SubFrame frame{frame_f};
298
299 if (G.is_break || worker_status->stop) {
300 break;
301 }
302
303 job.scene->r.cfra = frame.frame();
304 job.scene->r.subframe = frame.subframe();
305
307
309
311
312 const std::string frame_file_name = bake::frame_to_file_name(frame);
313
314 for (NodeBakeRequest &request : job.bake_requests) {
315 NodesModifierData &nmd = *request.nmd;
316 bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
317 const bake::NodeBakeCache *bake_cache = modifier_cache.get_node_bake_cache(request.bake_id);
318 if (bake_cache == nullptr) {
319 continue;
320 }
321 if (bake_cache->frames.is_empty()) {
322 continue;
323 }
324 const bake::FrameCache &frame_cache = *bake_cache->frames.last();
325 if (frame_cache.frame != frame) {
326 continue;
327 }
328
329 int64_t &written_size = size_by_bake.lookup_or_add(&request, 0);
330
331 if (request.path.has_value()) {
332 char meta_path[FILE_MAX];
333 BLI_path_join(meta_path,
334 sizeof(meta_path),
335 request.path->meta_dir.c_str(),
336 (frame_file_name + ".json").c_str());
338 bake::DiskBlobWriter blob_writer{request.path->blobs_dir, frame_file_name};
339 fstream meta_file{meta_path, std::ios::out};
340 bake::serialize_bake(frame_cache.state, blob_writer, *request.blob_sharing, meta_file);
341 written_size += blob_writer.written_size();
342 written_size += meta_file.tellp();
343 }
344 else {
345 PackedBake &packed_data = packed_data_by_bake.lookup_or_add_default(&request);
346
347 bake::MemoryBlobWriter blob_writer{frame_file_name};
348 std::ostringstream meta_file{std::ios::binary};
349 bake::serialize_bake(frame_cache.state, blob_writer, *request.blob_sharing, meta_file);
350
351 packed_data.meta_files.append({frame_file_name + ".json", meta_file.str()});
353 blob_writer.get_stream_by_name();
354 for (auto &&item : blob_stream_by_name.items()) {
355 std::string data = item.value.stream->str();
356 if (data.empty()) {
357 continue;
358 }
359 if (data.size() > PACKED_FILE_MAX_SIZE) {
360 job.error_message = TIP_("A file is too large to be packed (>2GB).");
361 return;
362 }
363 packed_data.blob_files.append({item.key, std::move(data)});
364 }
365 written_size += blob_writer.written_size();
366 written_size += meta_file.tellp();
367 }
368 }
369
370 worker_status->progress += progress_per_frame;
371 worker_status->do_update = true;
372 }
373
374 /* Update bake sizes. */
375 for (NodeBakeRequest &request : job.bake_requests) {
376 NodesModifierBake *bake = request.nmd->find_bake(request.bake_id);
377 bake->bake_size = size_by_bake.lookup_default(&request, 0);
378 }
379
380 /* Store gathered data as packed data. */
381 for (NodeBakeRequest &request : job.bake_requests) {
382 NodesModifierBake *bake = request.nmd->find_bake(request.bake_id);
383
384 PackedBake *packed_data = packed_data_by_bake.lookup_ptr(&request);
385 if (!packed_data) {
386 continue;
387 }
388
390
391 packed_bake->meta_files_num = packed_data->meta_files.size();
392 packed_bake->blob_files_num = packed_data->blob_files.size();
393
395 __func__);
397 __func__);
398
399 auto transfer_to_bake =
400 [&](NodesModifierBakeFile *bake_files, MemoryBakeFile *memory_bake_files, const int num) {
401 for (const int i : IndexRange(num)) {
402 NodesModifierBakeFile &bake_file = bake_files[i];
403 MemoryBakeFile &memory = memory_bake_files[i];
404 bake_file.name = BLI_strdup_null(memory.name.c_str());
405 const int64_t data_size = memory.data.size();
406 if (data_size == 0) {
407 continue;
408 }
409 const auto *sharing_info = new blender::ImplicitSharedValue<std::string>(
410 std::move(memory.data));
411 const void *data = sharing_info->data.data();
412 bake_file.packed_file = BKE_packedfile_new_from_memory(data, data_size, sharing_info);
413 }
414 };
415
416 transfer_to_bake(
417 packed_bake->meta_files, packed_data->meta_files.data(), packed_bake->meta_files_num);
418 transfer_to_bake(
419 packed_bake->blob_files, packed_data->blob_files.data(), packed_bake->blob_files_num);
420
421 /* Should have been freed before. */
422 BLI_assert(bake->packed == nullptr);
423 bake->packed = packed_bake;
424 }
425
426 /* Tag simulations as being baked. */
427 for (NodeBakeRequest &request : job.bake_requests) {
428 if (request.node_type != GEO_NODE_SIMULATION_OUTPUT) {
429 continue;
430 }
431 NodesModifierData &nmd = *request.nmd;
432 if (bake::SimulationNodeCache *node_cache = nmd.runtime->cache->get_simulation_node_cache(
433 request.bake_id))
434 {
435 if (!node_cache->bake.frames.is_empty()) {
436 /* Tag the caches as being baked so that they are not changed anymore. */
437 node_cache->cache_status = bake::CacheStatus::Baked;
438 }
439 }
441 }
442
443 job.scene->r.cfra = old_frame;
445
446 worker_status->progress = 1.0f;
447 worker_status->do_update = true;
448}
449
450static void bake_geometry_nodes_endjob(void *customdata)
451{
452 BakeGeometryNodesJob &job = *static_cast<BakeGeometryNodesJob *>(customdata);
453 WM_locked_interface_set(job.wm, false);
454 G.is_rendering = false;
458
459 if (!job.error_message.empty()) {
460 for (NodeBakeRequest &request : job.bake_requests) {
461 reset_old_bake_cache(request);
462 try_delete_bake(job.bmain, *request.object, *request.nmd, request.bake_id, job.op->reports);
463 }
464 BKE_report(job.op->reports, RPT_ERROR, job.error_message.c_str());
465 }
466}
467
469{
471 &bake.data_blocks_num,
472 &bake.active_data_block,
473 [](NodesModifierDataBlock *data_block) {
474 nodes_modifier_data_block_destruct(data_block, true);
475 });
476}
477
479{
480 switch (request.node_type) {
482 if (bake::SimulationNodeCache *node_cache =
483 request.nmd->runtime->cache->get_simulation_node_cache(request.bake_id))
484 {
485 node_cache->reset();
486 }
487 break;
488 }
489 case GEO_NODE_BAKE: {
490 if (bake::BakeNodeCache *node_cache = request.nmd->runtime->cache->get_bake_node_cache(
491 request.bake_id))
492 {
493 node_cache->reset();
494 }
495 break;
496 }
497 }
498}
499
500static void try_delete_bake(
501 Main *bmain, Object &object, NodesModifierData &nmd, const int bake_id, ReportList *reports)
502{
503 if (!nmd.runtime->cache) {
504 return;
505 }
506 bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
507 std::lock_guard lock{modifier_cache.mutex};
508 if (auto *node_cache = modifier_cache.simulation_cache_by_id.lookup_ptr(bake_id)) {
509 (*node_cache)->reset();
510 }
511 else if (auto *node_cache = modifier_cache.bake_cache_by_id.lookup_ptr(bake_id)) {
512 (*node_cache)->reset();
513 }
514 NodesModifierBake *bake = nmd.find_bake(bake_id);
515 if (!bake) {
516 return;
517 }
519
520 if (bake->packed) {
522 bake->packed = nullptr;
523 }
524
525 const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
526 *bmain, object, nmd, bake_id);
527 if (!bake_path) {
528 return;
529 }
530 const char *meta_dir = bake_path->meta_dir.c_str();
531 if (BLI_exists(meta_dir)) {
532 if (BLI_delete(meta_dir, true, true)) {
533 BKE_reportf(reports, RPT_ERROR, "Failed to remove metadata directory %s", meta_dir);
534 }
535 }
536 const char *blobs_dir = bake_path->blobs_dir.c_str();
537 if (BLI_exists(blobs_dir)) {
538 if (BLI_delete(blobs_dir, true, true)) {
539 BKE_reportf(reports, RPT_ERROR, "Failed to remove blobs directory %s", blobs_dir);
540 }
541 }
542 if (bake_path->bake_dir.has_value()) {
543 const char *zone_bake_dir = bake_path->bake_dir->c_str();
544 /* Try to delete zone bake directory if it is empty. */
545 BLI_delete(zone_bake_dir, true, false);
546 }
547 if (const std::optional<std::string> modifier_bake_dir = bake::get_modifier_bake_path(
548 *bmain, object, nmd))
549 {
550 /* Try to delete modifier bake directory if it is empty. */
551 BLI_delete(modifier_bake_dir->c_str(), true, false);
552 }
553}
554
565
568 wmOperator *op,
569 const BakeRequestsMode mode)
570{
571 Main *bmain = CTX_data_main(C);
572 for (NodeBakeRequest &request : requests) {
573 reset_old_bake_cache(request);
574 if (NodesModifierBake *bake = request.nmd->find_bake(request.bake_id)) {
576 }
577 try_delete_bake(bmain, *request.object, *request.nmd, request.bake_id, op->reports);
578 }
579
580 BakeGeometryNodesJob *job = MEM_new<BakeGeometryNodesJob>(__func__);
581 job->wm = CTX_wm_manager(C);
582 job->bmain = CTX_data_main(C);
584 job->scene = CTX_data_scene(C);
585 job->bake_requests = std::move(requests);
586 job->op = op;
587 WM_locked_interface_set(job->wm, true);
588
589 if (mode == BakeRequestsMode::Sync) {
590 wmJobWorkerStatus worker_status{};
591 bake_geometry_nodes_startjob(job, &worker_status);
593 MEM_delete(job);
594 return OPERATOR_FINISHED;
595 }
596
597 wmJob *wm_job = WM_jobs_get(job->wm,
599 job->scene,
600 "Baking nodes...",
603
605 wm_job, job, [](void *job) { MEM_delete(static_cast<BakeGeometryNodesJob *>(job)); });
609
613}
614
616 Scene &scene,
617 const Span<Object *> objects)
618{
620 for (Object *object : objects) {
621 if (!BKE_id_is_editable(&bmain, &object->id)) {
622 continue;
623 }
624 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
625 if (md->type != eModifierType_Nodes) {
626 continue;
627 }
629 continue;
630 }
631 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
632 if (!nmd->node_group) {
633 continue;
634 }
635 if (!nmd->runtime->cache) {
636 continue;
637 }
638 for (const bNestedNodeRef &nested_node_ref : nmd->node_group->nested_node_refs_span()) {
639 const int id = nested_node_ref.id;
640 const bNode *node = nmd->node_group->find_nested_node(id);
642 continue;
643 }
644 NodeBakeRequest request;
645 request.object = object;
646 request.nmd = nmd;
647 request.bake_id = id;
648 request.node_type = node->type_legacy;
649 request.blob_sharing = std::make_unique<bake::BlobWriteSharing>();
651 request.path = bake::get_node_bake_path(bmain, *object, *nmd, id);
652 }
653 std::optional<IndexRange> frame_range = bake::get_node_bake_frame_range(
654 scene, *object, *nmd, id);
655 if (!frame_range) {
656 continue;
657 }
658 request.frame_start = frame_range->first();
659 request.frame_end = frame_range->last();
660
661 requests.append(std::move(request));
662 }
663 }
664 }
665 return requests;
666}
667
669{
670 Scene *scene = CTX_data_scene(C);
671 Main *bmain = CTX_data_main(C);
672
673 Vector<Object *> objects;
674 if (RNA_boolean_get(op->ptr, "selected")) {
675 CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
676 objects.append(object);
677 }
679 }
680 else {
681 if (Object *object = CTX_data_active_object(C)) {
682 objects.append(object);
683 }
684 }
685
686 return collect_simulations_to_bake(*bmain, *scene, objects);
687}
688
694
697 {
698 /* Normalize the paths so we can compare them. */
699 DynamicStackBuffer<256> norm_buf(s.size() + 1, 8);
700 memcpy(norm_buf.buffer(), s.data(), s.size() + 1);
701 char *norm = static_cast<char *>(norm_buf.buffer());
702
704
705 /* Strip ending slash. */
707
709 return get_default_hash(norm);
710 }
711};
712
714 bool operator()(const StringRef a, const StringRef b) const
715 {
716 return BLI_path_cmp_normalized(a.data(), b.data()) == 0;
717 }
718};
719
720static bool bake_directory_has_data(const StringRefNull absolute_bake_dir)
721{
722 char meta_dir[FILE_MAX];
723 BLI_path_join(meta_dir, sizeof(meta_dir), absolute_bake_dir.c_str(), "meta");
724 char blobs_dir[FILE_MAX];
725 BLI_path_join(blobs_dir, sizeof(blobs_dir), absolute_bake_dir.c_str(), "blobs");
726
727 if (!BLI_is_dir(meta_dir) || !BLI_is_dir(blobs_dir)) {
728 return false;
729 }
730
731 return true;
732}
733
735{
737 return true;
738 }
739 for (const NodesModifierBake &bake : Span{nmd.bakes, nmd.bakes_num}) {
740 if (bake.bake_target == NODES_MODIFIER_BAKE_TARGET_DISK) {
741 return true;
742 }
743 }
744 return false;
745}
746
748 Object &object,
750 wmOperator *op)
751{
752 const bool bake_directory_set = !StringRef(nmd.bake_directory).is_empty();
753 if (bake_directory_set) {
754 return;
755 }
756 if (!may_have_disk_bake(nmd)) {
757 return;
758 }
759
760 Main *bmain = CTX_data_main(C);
761
763 RPT_INFO,
764 "Bake directory of object %s, modifier %s is empty, setting default path",
765 object.id.name + 2,
766 nmd.modifier.name);
767
769 bake::get_default_modifier_bake_directory(*bmain, object, nmd).c_str());
770}
771
773 wmOperator *op,
774 const Span<Object *> objects)
775{
776 Main *bmain = CTX_data_main(C);
777
778 for (Object *object : objects) {
779 if (!BKE_id_is_editable(bmain, &object->id)) {
780 continue;
781 }
782
783 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
784 if (md->type != eModifierType_Nodes) {
785 continue;
786 }
787 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
789 }
790 }
791}
792
793/* Map for counting path references. */
794using PathUsersMap = Map<std::string,
795 int,
796 default_inline_buffer_capacity(sizeof(std::string)),
800
802{
803 Main *bmain = CTX_data_main(C);
804
805 PathUsersMap path_users;
806 for (const Object *object : objects) {
807 const char *base_path = ID_BLEND_PATH(bmain, &object->id);
808
809 LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) {
810 if (md->type != eModifierType_Nodes) {
811 continue;
812 }
813 const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
814 if (StringRef(nmd->bake_directory).is_empty()) {
815 continue;
816 }
817
818 char absolute_bake_dir[FILE_MAX];
819 STRNCPY(absolute_bake_dir, nmd->bake_directory);
820 BLI_path_abs(absolute_bake_dir, base_path);
821 path_users.add_or_modify(
822 absolute_bake_dir, [](int *value) { *value = 1; }, [](int *value) { ++(*value); });
823 }
824 }
825
826 return path_users;
827}
828
830 wmOperator *op,
831 const wmEvent * /*event*/)
832{
833 Vector<Object *> objects;
834 if (RNA_boolean_get(op->ptr, "selected")) {
835 CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
836 objects.append(object);
837 }
839 }
840 else {
841 if (Object *object = CTX_data_active_object(C)) {
842 objects.append(object);
843 }
844 }
845
846 /* Set empty paths to default if necessary. */
847 bake_simulation_validate_paths(C, op, objects);
848
849 PathUsersMap path_users = bake_simulation_get_path_users(C, objects);
850 bool has_path_conflict = false;
851 bool has_existing_bake_data = false;
852 for (const auto &item : path_users.items()) {
853 /* Check if multiple caches are writing to the same bake directory. */
854 if (item.value > 1) {
856 RPT_ERROR,
857 "Path conflict: %d caches set to path %s",
858 item.value,
859 item.key.data());
860 has_path_conflict = true;
861 }
862
863 /* Check if path exists and contains bake data already. */
864 if (bake_directory_has_data(item.key.data())) {
865 has_existing_bake_data = true;
866 }
867 }
868
869 if (has_path_conflict) {
871 return OPERATOR_CANCELLED;
872 }
873 if (has_existing_bake_data) {
875 op,
876 IFACE_("Overwrite existing bake data?"),
877 nullptr,
878 IFACE_("Bake"),
880 false);
881 }
883 return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Async);
884}
885
895
897{
898 Main *bmain = CTX_data_main(C);
899
900 Vector<Object *> objects;
901 if (RNA_boolean_get(op->ptr, "selected")) {
902 CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
903 objects.append(object);
904 }
906 }
907 else {
908 if (Object *object = CTX_data_active_object(C)) {
909 objects.append(object);
910 }
911 }
912
913 if (objects.is_empty()) {
914 return OPERATOR_CANCELLED;
915 }
916
917 for (Object *object : objects) {
918 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
919 if (md->type == eModifierType_Nodes) {
920 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
921 for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
922 try_delete_bake(bmain, *object, *nmd, bake.id, op->reports);
923 }
924 }
925 }
926
928 }
929
931
932 return OPERATOR_FINISHED;
933}
934
936{
937 Main *bmain = CTX_data_main(C);
938 Scene *scene = CTX_data_scene(C);
939 Object *object = reinterpret_cast<Object *>(
941 if (object == nullptr) {
942 return {};
943 }
944 std::string modifier_name = RNA_string_get(op->ptr, "modifier_name");
946 if (md == nullptr) {
947 return {};
948 }
949 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
950 if (nmd.node_group == nullptr) {
951 return {};
952 }
954 BKE_report(op->reports, RPT_ERROR, "Modifier containing the node is disabled");
955 return {};
956 }
957
959
960 const int bake_id = RNA_int_get(op->ptr, "bake_id");
961 const bNode *node = nmd.node_group->find_nested_node(bake_id);
962 if (node == nullptr) {
963 return {};
964 }
966 return {};
967 }
968
969 NodeBakeRequest request;
970 request.object = object;
971 request.nmd = &nmd;
972 request.bake_id = bake_id;
973 request.node_type = node->type_legacy;
974 request.blob_sharing = std::make_unique<bake::BlobWriteSharing>();
975
976 const NodesModifierBake *bake = nmd.find_bake(bake_id);
977 if (!bake) {
978 return {};
979 }
980 if (bake::get_node_bake_target(*object, nmd, bake_id) == NODES_MODIFIER_BAKE_TARGET_DISK) {
981 request.path = bake::get_node_bake_path(*bmain, *object, nmd, bake_id);
982 if (!request.path) {
984 RPT_INFO,
985 "Cannot determine bake location on disk. Falling back to packed bake.");
986 }
987 }
988
989 if (node->type_legacy == GEO_NODE_BAKE && bake->bake_mode == NODES_MODIFIER_BAKE_MODE_STILL) {
990 const int current_frame = scene->r.cfra;
991 request.frame_start = current_frame;
992 request.frame_end = current_frame;
993 /* Delete old bake because otherwise this wouldn't be a still frame bake. This is not done for
994 * other bakes to avoid loosing data when starting a bake. */
995 try_delete_bake(bmain, *object, nmd, bake_id, op->reports);
996 }
997 else {
998 const std::optional<IndexRange> frame_range = bake::get_node_bake_frame_range(
999 *scene, *object, nmd, bake_id);
1000 if (!frame_range.has_value()) {
1001 return {};
1002 }
1003 if (frame_range->is_empty()) {
1004 return {};
1005 }
1006 request.frame_start = frame_range->first();
1007 request.frame_end = frame_range->last();
1008 }
1009
1010 Vector<NodeBakeRequest> requests;
1011 requests.append(std::move(request));
1012 return requests;
1013}
1014
1016 wmOperator *op,
1017 const wmEvent * /*event*/)
1018{
1020 if (requests.is_empty()) {
1021 return OPERATOR_CANCELLED;
1022 }
1023 return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Async);
1024}
1025
1027{
1029 if (requests.is_empty()) {
1030 return OPERATOR_CANCELLED;
1031 }
1032 return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Sync);
1033}
1034
1044
1046{
1047 Main *bmain = CTX_data_main(C);
1048 Object *object = reinterpret_cast<Object *>(
1050 if (object == nullptr) {
1051 return OPERATOR_CANCELLED;
1052 }
1053 std::string modifier_name = RNA_string_get(op->ptr, "modifier_name");
1055 if (md == nullptr) {
1056 return OPERATOR_CANCELLED;
1057 }
1058 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
1059 const int bake_id = RNA_int_get(op->ptr, "bake_id");
1060
1061 try_delete_bake(bmain, *object, nmd, bake_id, op->reports);
1062
1065 WM_main_add_notifier(NC_NODE, nullptr);
1066 return OPERATOR_FINISHED;
1067}
1068
1070{
1071 Main *bmain = CTX_data_main(C);
1072 Object *object = reinterpret_cast<Object *>(
1074 if (object == nullptr) {
1075 return OPERATOR_CANCELLED;
1076 }
1077 std::string modifier_name = RNA_string_get(op->ptr, "modifier_name");
1079 if (md == nullptr) {
1080 return OPERATOR_CANCELLED;
1081 }
1082 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
1083 const int bake_id = RNA_int_get(op->ptr, "bake_id");
1084
1085 const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
1086 *bmain, *object, nmd, bake_id);
1087 if (!bake_path) {
1088 return OPERATOR_CANCELLED;
1089 }
1090 NodesModifierBake *bake = nmd.find_bake(bake_id);
1091 if (!bake) {
1092 return OPERATOR_CANCELLED;
1093 }
1094
1095 bake::pack_geometry_nodes_bake(*bmain, op->reports, *object, nmd, *bake);
1096
1098 WM_main_add_notifier(NC_NODE, nullptr);
1099 return OPERATOR_FINISHED;
1100}
1101
1103 wmOperator *op,
1104 const wmEvent * /*event*/)
1105{
1106 uiPopupMenu *pup;
1107 uiLayout *layout;
1108
1109 pup = UI_popup_menu_begin(C, IFACE_("Unpack"), ICON_NONE);
1110 layout = UI_popup_menu_layout(pup);
1111
1113 layout->op_enum(op->type->idname,
1114 "method",
1115 static_cast<IDProperty *>(op->ptr->data),
1117 UI_ITEM_NONE);
1118
1119 UI_popup_menu_end(C, pup);
1120
1121 return OPERATOR_INTERFACE;
1122}
1123
1125{
1126 Main *bmain = CTX_data_main(C);
1127 Object *object = reinterpret_cast<Object *>(
1129 if (object == nullptr) {
1130 return OPERATOR_CANCELLED;
1131 }
1132 std::string modifier_name = RNA_string_get(op->ptr, "modifier_name");
1134 if (md == nullptr) {
1135 return OPERATOR_CANCELLED;
1136 }
1137 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
1138 const int bake_id = RNA_int_get(op->ptr, "bake_id");
1139 NodesModifierBake *bake = nmd.find_bake(bake_id);
1140 if (!bake) {
1141 return OPERATOR_CANCELLED;
1142 }
1143
1144 const ePF_FileStatus method = ePF_FileStatus(RNA_enum_get(op->ptr, "method"));
1145
1147 *bmain, op->reports, *object, nmd, *bake, method);
1149 return OPERATOR_CANCELLED;
1150 }
1151
1153 WM_main_add_notifier(NC_NODE, nullptr);
1154 return OPERATOR_FINISHED;
1155}
1156
1158{
1159 ot->name = "Calculate Simulation to Frame";
1160 ot->description =
1161 "Calculate simulations in geometry nodes modifiers from the start to current frame";
1162 ot->idname = __func__;
1163
1164 ot->invoke = simulate_to_frame_invoke;
1165 ot->modal = simulate_to_frame_modal;
1166 ot->poll = simulate_to_frame_poll;
1167
1168 RNA_def_boolean(ot->srna,
1169 "selected",
1170 false,
1171 "Selected",
1172 "Calculate all selected objects instead of just the active object");
1173}
1174
1176{
1177 ot->name = "Bake Simulation";
1178 ot->description = "Bake simulations in geometry nodes modifiers";
1179 ot->idname = __func__;
1180
1181 ot->exec = bake_simulation_exec;
1182 ot->invoke = bake_simulation_invoke;
1183 ot->modal = bake_simulation_modal;
1184 ot->poll = bake_simulation_poll;
1185
1186 RNA_def_boolean(ot->srna, "selected", false, "Selected", "Bake cache on all selected objects");
1187}
1188
1190{
1191 ot->name = "Delete Cached Simulation";
1192 ot->description = "Delete cached/baked simulations in geometry nodes modifiers";
1193 ot->idname = __func__;
1194
1197
1198 RNA_def_boolean(ot->srna, "selected", false, "Selected", "Delete cache on all selected objects");
1199}
1200
1202{
1204
1205 RNA_def_string(ot->srna,
1206 "modifier_name",
1207 nullptr,
1208 0,
1209 "Modifier Name",
1210 "Name of the modifier that contains the node");
1212 ot->srna, "bake_id", 0, 0, INT32_MAX, "Bake ID", "Nested node id of the node", 0, INT32_MAX);
1213}
1214
1216{
1217 ot->name = "Bake Geometry Node";
1218 ot->description = "Bake a single bake node or simulation";
1219 ot->idname = "OBJECT_OT_geometry_node_bake_single";
1220
1221 ot->invoke = bake_single_node_invoke;
1222 ot->exec = bake_single_node_exec;
1223 ot->modal = bake_single_node_modal;
1224
1226}
1227
1229{
1230 ot->name = "Delete Geometry Node Bake";
1231 ot->description = "Delete baked data of a single bake node or simulation";
1232 ot->idname = "OBJECT_OT_geometry_node_bake_delete_single";
1233
1235
1237}
1238
1240{
1241 ot->name = "Pack Geometry Node Bake";
1242 ot->description = "Pack baked data from disk into the .blend file";
1243 ot->idname = "OBJECT_OT_geometry_node_bake_pack_single";
1244
1245 ot->exec = pack_single_bake_exec;
1246
1248}
1249
1251{
1252 ot->name = "Unpack Geometry Node Bake";
1253 ot->description = "Unpack baked data from the .blend file to disk";
1254 ot->idname = "OBJECT_OT_geometry_node_bake_unpack_single";
1255
1257 ot->invoke = unpack_single_bake_invoke;
1258
1260
1261 static const EnumPropertyItem method_items[] = {
1262 {PF_USE_LOCAL,
1263 "USE_LOCAL",
1264 0,
1265 "Use bake from current directory (create when necessary)",
1266 ""},
1268 "WRITE_LOCAL",
1269 0,
1270 "Write bake to current directory (overwrite existing bake)",
1271 ""},
1273 "USE_ORIGINAL",
1274 0,
1275 "Use bake in original location (create when necessary)",
1276 ""},
1278 "WRITE_ORIGINAL",
1279 0,
1280 "Write bake to original location (overwrite existing file)",
1281 ""},
1282 {0, nullptr, 0, nullptr, nullptr},
1283 };
1284
1285 RNA_def_enum(ot->srna, "method", method_items, PF_USE_LOCAL, "Method", "How to unpack");
1286}
1287
1288} // namespace blender::ed::object::bake_simulation
#define CTX_DATA_BEGIN(C, Type, instance, member)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
wmWindow * CTX_wm_window(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define CTX_DATA_END
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2523
bool BKE_modifier_is_enabled(const Scene *scene, ModifierData *md, int required_mode)
ModifierData * BKE_modifiers_findby_name(const Object *ob, const char *name)
#define GEO_NODE_SIMULATION_OUTPUT
#define GEO_NODE_BAKE
PackedFile * BKE_packedfile_new_from_memory(const void *mem, int memlen, const blender::ImplicitSharingInfo *sharing_info=nullptr)
constexpr int64_t PACKED_FILE_MAX_SIZE
ePF_FileStatus
@ PF_USE_ORIGINAL
@ PF_USE_LOCAL
@ PF_WRITE_ORIGINAL
@ PF_WRITE_LOCAL
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2700
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:360
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:443
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:452
File and directory operations.
#define LISTBASE_FOREACH(type, var, list)
ATTR_WARN_UNUSED_RESULT const size_t num
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
void BLI_path_slash_native(char *path) ATTR_NONNULL(1)
int BLI_path_normalize(char *path) ATTR_NONNULL(1)
#define BLI_path_join(...)
int BLI_path_cmp_normalized(const char *p1, const char *p2) ATTR_NONNULL(1
void BLI_path_slash_rstrip(char *path) ATTR_NONNULL(1)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * BLI_strdup_null(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_MALLOC
Definition string.cc:46
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define ELEM(...)
#define TIP_(msgid)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_id_tag_update_for_side_effect_request(Depsgraph *depsgraph, ID *id, unsigned int flags)
void DEG_time_tag_update(Main *bmain)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
#define ID_BLEND_PATH(_bmain, _id)
Definition DNA_ID.h:685
@ ID_OB
@ NODES_MODIFIER_BAKE_TARGET_DISK
@ eModifierMode_Realtime
@ eModifierType_Nodes
@ NODES_MODIFIER_BAKE_MODE_STILL
@ OB_FLAG_USE_SIMULATION_CACHE
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
bool ED_operator_object_active(bContext *C)
#define C
Definition RandGen.cpp:29
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void UI_popup_menu_reports(bContext *C, ReportList *reports) ATTR_NONNULL()
@ ALERT_ICON_NONE
#define UI_ITEM_NONE
@ WM_JOB_TYPE_CALCULATE_SIMULATION_NODES
Definition WM_api.hh:1809
@ WM_JOB_TYPE_BAKE_GEOMETRY_NODES
Definition WM_api.hh:1810
@ WM_JOB_PROGRESS
Definition WM_api.hh:1766
#define NC_NODE
Definition WM_types.hh:394
#define ND_DISPLAY
Definition WM_types.hh:491
#define ND_MODIFIER
Definition WM_types.hh:462
#define ND_SPACE_VIEW3D
Definition WM_types.hh:528
#define NC_OBJECT
Definition WM_types.hh:379
#define NS_VIEW3D_SHADING
Definition WM_types.hh:577
#define NC_SPACE
Definition WM_types.hh:392
volatile int lock
BMesh const char void * data
BPy_StructRNA * depsgraph
long long int int64_t
unsigned long long int uint64_t
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition btVector3.h:263
void append(const T &value)
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:639
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
auto add_or_modify(const Key &key, const CreateValueF &create_value, const ModifyValueF &modify_value) -> decltype(create_value(nullptr))
Definition BLI_map.hh:481
ItemIterator items() const &
Definition BLI_map.hh:902
Value & lookup_or_add(const Key &key, const Value &value)
Definition BLI_map.hh:588
constexpr bool is_empty() const
constexpr int64_t size() const
constexpr const char * data() const
constexpr const char * c_str() const
void append(const T &value)
bool is_empty() const
const Map< std::string, OutputStream > & get_stream_by_name() const
nullptr float
#define INT32_MAX
#define INT32_MIN
static const char * modifier_name[LS_MODIFIER_NUM]
Definition linestyle.cc:689
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
#define G(x, y, z)
std::optional< NodesModifierBakeTarget > get_node_bake_target(const Object &object, const NodesModifierData &nmd, int node_id)
std::string get_default_modifier_bake_directory(const Main &bmain, const Object &object, const NodesModifierData &nmd)
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 serialize_bake(const BakeState &bake_state, BlobWriter &blob_writer, BlobWriteSharing &blob_sharing, std::ostream &r_stream)
UnpackGeometryNodesBakeResult unpack_geometry_nodes_bake(Main &bmain, ReportList *reports, Object &object, NodesModifierData &nmd, NodesModifierBake &bake, ePF_FileStatus how)
PackGeometryNodesBakeResult pack_geometry_nodes_bake(Main &bmain, ReportList *reports, Object &object, NodesModifierData &nmd, NodesModifierBake &bake)
std::string frame_to_file_name(const SubFrame &frame)
std::optional< std::string > get_modifier_bake_path(const Main &bmain, const Object &object, const NodesModifierData &nmd)
void clear(T **items, int *items_num, int *active_index, void(*destruct_item)(T *))
void OBJECT_OT_simulation_nodes_cache_bake(wmOperatorType *ot)
static wmOperatorStatus bake_single_node_exec(bContext *C, wmOperator *op)
static void bake_geometry_nodes_startjob(void *customdata, wmJobWorkerStatus *worker_status)
static void clear_requested_bakes_in_modifier_cache(BakeGeometryNodesJob &job)
static void bake_geometry_nodes_endjob(void *customdata)
static void simulate_to_frame_endjob(void *customdata)
static void bake_simulation_validate_paths(bContext *C, wmOperator *op, const Span< Object * > objects)
static wmOperatorStatus unpack_single_bake_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus unpack_single_bake_exec(bContext *C, wmOperator *op)
static wmOperatorStatus bake_simulation_modal(bContext *C, wmOperator *, const wmEvent *)
static void simulate_to_frame_startjob(void *customdata, wmJobWorkerStatus *worker_status)
void OBJECT_OT_geometry_node_bake_delete_single(wmOperatorType *ot)
static wmOperatorStatus simulate_to_frame_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus bake_simulation_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus simulate_to_frame_modal(bContext *C, wmOperator *, const wmEvent *)
void OBJECT_OT_geometry_node_bake_unpack_single(wmOperatorType *ot)
static void single_bake_operator_props(wmOperatorType *ot)
void OBJECT_OT_geometry_node_bake_pack_single(wmOperatorType *ot)
static void initialize_modifier_bake_directory_if_necessary(bContext *C, Object &object, NodesModifierData &nmd, wmOperator *op)
static wmOperatorStatus delete_baked_simulation_exec(bContext *C, wmOperator *op)
static Vector< NodeBakeRequest > bake_simulation_gather_requests(bContext *C, wmOperator *op)
static wmOperatorStatus bake_single_node_modal(bContext *C, wmOperator *, const wmEvent *)
static PathUsersMap bake_simulation_get_path_users(bContext *C, const Span< Object * > objects)
static wmOperatorStatus bake_simulation_exec(bContext *C, wmOperator *op)
static bool may_have_disk_bake(const NodesModifierData &nmd)
void OBJECT_OT_geometry_node_bake_single(wmOperatorType *ot)
static wmOperatorStatus pack_single_bake_exec(bContext *C, wmOperator *op)
static wmOperatorStatus delete_single_bake_exec(bContext *C, wmOperator *op)
static Vector< NodeBakeRequest > collect_simulations_to_bake(Main &bmain, Scene &scene, const Span< Object * > objects)
static wmOperatorStatus bake_single_node_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void request_bakes_in_modifier_cache(BakeGeometryNodesJob &job)
void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot)
static void reset_old_bake_cache(NodeBakeRequest &request)
static bool bake_directory_has_data(const StringRefNull absolute_bake_dir)
Map< std::string, int, default_inline_buffer_capacity(sizeof(std::string)), DefaultProbingStrategy, PathStringHash, PathStringEquality > PathUsersMap
static Vector< NodeBakeRequest > bake_single_node_gather_bake_request(bContext *C, wmOperator *op)
static wmOperatorStatus start_bake_job(bContext *C, Vector< NodeBakeRequest > requests, wmOperator *op, const BakeRequestsMode mode)
static void try_delete_bake(Main *bmain, Object &object, NodesModifierData &nmd, const int bake_id, ReportList *reports)
void OBJECT_OT_simulation_nodes_cache_delete(wmOperatorType *ot)
static void clear_data_block_references(NodesModifierBake &bake)
Object * context_active_object(const bContext *C)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
constexpr int64_t default_inline_buffer_capacity(size_t element_size)
PythonProbingStrategy<> DefaultProbingStrategy
void nodes_modifier_packed_bake_free(NodesModifierPackedBake *packed_bake)
const char * name
int RNA_int_get(PointerRNA *ptr, const char *name)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
struct bNodeTree * node_group
NodesModifierRuntimeHandle * runtime
NodesModifierBake * bakes
NodesModifierBakeFile * meta_files
NodesModifierBakeFile * blob_files
void * data
Definition RNA_types.hh:53
struct RenderData r
int16_t type_legacy
float subframe() const
Map< int, std::unique_ptr< SimulationNodeCache > > simulation_cache_by_id
Map< int, std::unique_ptr< BakeNodeCache > > bake_cache_by_id
NodeBakeCache * get_node_bake_cache(const int id)
Vector< std::unique_ptr< FrameCache > > frames
std::unique_ptr< bake::BlobWriteSharing > blob_sharing
bool operator()(const StringRef a, const StringRef b) const
void op_enum(blender::StringRefNull opname, blender::StringRefNull propname, IDProperty *properties, blender::wm::OpCallContext context, eUI_Item_Flag flag, const int active=-1)
void operator_context_set(blender::wm::OpCallContext opcontext)
const char * idname
Definition WM_types.hh:1035
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_locked_interface_set(wmWindowManager *wm, bool lock)
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:376
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:479
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:211
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:388
bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type)
Definition wm_jobs.cc:247
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:360
ID * WM_operator_properties_id_lookup_from_name_or_session_uid(Main *bmain, PointerRNA *ptr, const ID_Type type)
void WM_operator_properties_id_lookup(wmOperatorType *ot, const bool add_name_prop)
wmOperatorStatus WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)