Blender V4.5
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"
49
50namespace bake = blender::bke::bake;
51
53
55{
57 return false;
58 }
59 return true;
60}
61
71
72static void simulate_to_frame_startjob(void *customdata, wmJobWorkerStatus *worker_status)
73{
74 SimulateToFrameJob &job = *static_cast<SimulateToFrameJob *>(customdata);
75 G.is_rendering = true;
76 G.is_break = false;
77 WM_set_locked_interface(job.wm, true);
78
79 Vector<Object *> objects_to_calc;
80 for (Object *object : job.objects) {
81 if (!BKE_id_is_editable(job.bmain, &object->id)) {
82 continue;
83 }
84 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
85 if (md->type != eModifierType_Nodes) {
86 continue;
87 }
88 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
89 if (!nmd->runtime->cache) {
90 continue;
91 }
92 for (auto item : nmd->runtime->cache->simulation_cache_by_id.items()) {
93 if (item.value->cache_status != bake::CacheStatus::Baked) {
94 item.value->reset();
95 }
96 }
97 }
98 objects_to_calc.append(object);
99 }
100
101 worker_status->progress = 0.0f;
102 worker_status->do_update = true;
103
104 const float frame_step_size = 1.0f;
105 const float progress_per_frame = 1.0f /
106 (float(job.end_frame - job.start_frame + 1) / frame_step_size);
107 const int old_frame = job.scene->r.cfra;
108
109 for (float frame_f = job.start_frame; frame_f <= job.end_frame; frame_f += frame_step_size) {
110 const SubFrame frame{frame_f};
111
112 if (G.is_break || worker_status->stop) {
113 break;
114 }
115
116 job.scene->r.cfra = frame.frame();
117 job.scene->r.subframe = frame.subframe();
118
120
121 worker_status->progress += progress_per_frame;
122 worker_status->do_update = true;
123 }
124
125 job.scene->r.cfra = old_frame;
127
128 worker_status->progress = 1.0f;
129 worker_status->do_update = true;
130}
131
132static void simulate_to_frame_endjob(void *customdata)
133{
134 SimulateToFrameJob &job = *static_cast<SimulateToFrameJob *>(customdata);
135 WM_set_locked_interface(job.wm, false);
136 G.is_rendering = false;
138}
139
141 wmOperator *op,
142 const wmEvent * /*event*/)
143{
145 Scene *scene = CTX_data_scene(C);
147 Main *bmain = CTX_data_main(C);
148
149 SimulateToFrameJob *job = MEM_new<SimulateToFrameJob>(__func__);
150 job->wm = wm;
151 job->bmain = bmain;
152 job->depsgraph = depsgraph;
153 job->scene = scene;
154 job->start_frame = scene->r.sfra;
155 job->end_frame = scene->r.cfra;
156
157 if (RNA_boolean_get(op->ptr, "selected")) {
158 CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
159 job->objects.append(object);
160 }
162 }
163 else {
164 if (Object *object = CTX_data_active_object(C)) {
165 job->objects.append(object);
166 }
167 }
168
169 wmJob *wm_job = WM_jobs_get(wm,
172 "Calculate Simulation",
175
177 wm_job, job, [](void *job) { MEM_delete(static_cast<SimulateToFrameJob *>(job)); });
180 wm_job, simulate_to_frame_startjob, nullptr, nullptr, simulate_to_frame_endjob);
181
185}
186
197
199{
201 return false;
202 }
204 const bool use_frame_cache = ob->flag & OB_FLAG_USE_SIMULATION_CACHE;
205 if (!use_frame_cache) {
206 CTX_wm_operator_poll_msg_set(C, "Cache has to be enabled");
207 return false;
208 }
209 return true;
210}
211
217
219 std::optional<bake::BakePath> path;
222 std::unique_ptr<bake::BlobWriteSharing> blob_sharing;
223};
224
232
234{
235 for (NodeBakeRequest &request : job.bake_requests) {
236 request.nmd->runtime->cache->requested_bakes.add(request.bake_id);
237 /* Using #DEG_id_tag_update would tag this as user-modified which is not the case here and has
238 * the issue that it invalidates simulation caches. */
240 job.depsgraph, &request.object->id, ID_RECALC_GEOMETRY);
241 }
242}
243
245{
246 for (NodeBakeRequest &request : job.bake_requests) {
247 request.nmd->runtime->cache->requested_bakes.clear();
248 }
249}
250
251static void bake_geometry_nodes_startjob(void *customdata, wmJobWorkerStatus *worker_status)
252{
253 BakeGeometryNodesJob &job = *static_cast<BakeGeometryNodesJob *>(customdata);
254 G.is_rendering = true;
255 G.is_break = false;
256
257 int global_bake_start_frame = INT32_MAX;
258 int global_bake_end_frame = INT32_MIN;
259
260 for (NodeBakeRequest &request : job.bake_requests) {
261 global_bake_start_frame = std::min(global_bake_start_frame, request.frame_start);
262 global_bake_end_frame = std::max(global_bake_end_frame, request.frame_end);
263 }
264
265 worker_status->progress = 0.0f;
266 worker_status->do_update = true;
267
268 const int frames_to_bake = global_bake_end_frame - global_bake_start_frame + 1;
269
270 const float frame_step_size = 1.0f;
271 const float progress_per_frame = frame_step_size / frames_to_bake;
272 const int old_frame = job.scene->r.cfra;
273
274 struct MemoryBakeFile {
275 std::string name;
276 std::string data;
277 };
278
279 struct PackedBake {
280 Vector<MemoryBakeFile> meta_files;
281 Vector<MemoryBakeFile> blob_files;
282 };
283
284 Map<NodeBakeRequest *, PackedBake> packed_data_by_bake;
286
287 for (float frame_f = global_bake_start_frame; frame_f <= global_bake_end_frame;
288 frame_f += frame_step_size)
289 {
290 const SubFrame frame{frame_f};
291
292 if (G.is_break || worker_status->stop) {
293 break;
294 }
295
296 job.scene->r.cfra = frame.frame();
297 job.scene->r.subframe = frame.subframe();
298
300
302
304
305 const std::string frame_file_name = bake::frame_to_file_name(frame);
306
307 for (NodeBakeRequest &request : job.bake_requests) {
308 NodesModifierData &nmd = *request.nmd;
309 bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
310 const bake::NodeBakeCache *bake_cache = modifier_cache.get_node_bake_cache(request.bake_id);
311 if (bake_cache == nullptr) {
312 continue;
313 }
314 if (bake_cache->frames.is_empty()) {
315 continue;
316 }
317 const bake::FrameCache &frame_cache = *bake_cache->frames.last();
318 if (frame_cache.frame != frame) {
319 continue;
320 }
321
322 int64_t &written_size = size_by_bake.lookup_or_add(&request, 0);
323
324 if (request.path.has_value()) {
325 char meta_path[FILE_MAX];
326 BLI_path_join(meta_path,
327 sizeof(meta_path),
328 request.path->meta_dir.c_str(),
329 (frame_file_name + ".json").c_str());
331 bake::DiskBlobWriter blob_writer{request.path->blobs_dir, frame_file_name};
332 fstream meta_file{meta_path, std::ios::out};
333 bake::serialize_bake(frame_cache.state, blob_writer, *request.blob_sharing, meta_file);
334 written_size += blob_writer.written_size();
335 written_size += meta_file.tellp();
336 }
337 else {
338 PackedBake &packed_data = packed_data_by_bake.lookup_or_add_default(&request);
339
340 bake::MemoryBlobWriter blob_writer{frame_file_name};
341 std::ostringstream meta_file{std::ios::binary};
342 bake::serialize_bake(frame_cache.state, blob_writer, *request.blob_sharing, meta_file);
343
344 packed_data.meta_files.append({frame_file_name + ".json", meta_file.str()});
346 blob_writer.get_stream_by_name();
347 for (auto &&item : blob_stream_by_name.items()) {
348 std::string data = item.value.stream->str();
349 if (data.empty()) {
350 continue;
351 }
352 packed_data.blob_files.append({item.key, std::move(data)});
353 }
354 written_size += blob_writer.written_size();
355 written_size += meta_file.tellp();
356 }
357 }
358
359 worker_status->progress += progress_per_frame;
360 worker_status->do_update = true;
361 }
362
363 /* Update bake sizes. */
364 for (NodeBakeRequest &request : job.bake_requests) {
365 NodesModifierBake *bake = request.nmd->find_bake(request.bake_id);
366 bake->bake_size = size_by_bake.lookup_default(&request, 0);
367 }
368
369 /* Store gathered data as packed data. */
370 for (NodeBakeRequest &request : job.bake_requests) {
371 NodesModifierBake *bake = request.nmd->find_bake(request.bake_id);
372
373 PackedBake *packed_data = packed_data_by_bake.lookup_ptr(&request);
374 if (!packed_data) {
375 continue;
376 }
377
379
380 packed_bake->meta_files_num = packed_data->meta_files.size();
381 packed_bake->blob_files_num = packed_data->blob_files.size();
382
384 __func__);
386 __func__);
387
388 auto transfer_to_bake =
389 [&](NodesModifierBakeFile *bake_files, MemoryBakeFile *memory_bake_files, const int num) {
390 for (const int i : IndexRange(num)) {
391 NodesModifierBakeFile &bake_file = bake_files[i];
392 MemoryBakeFile &memory = memory_bake_files[i];
393 bake_file.name = BLI_strdup_null(memory.name.c_str());
394 const int64_t data_size = memory.data.size();
395 if (data_size == 0) {
396 continue;
397 }
398 const auto *sharing_info = new blender::ImplicitSharedValue<std::string>(
399 std::move(memory.data));
400 const void *data = sharing_info->data.data();
401 bake_file.packed_file = BKE_packedfile_new_from_memory(data, data_size, sharing_info);
402 }
403 };
404
405 transfer_to_bake(
406 packed_bake->meta_files, packed_data->meta_files.data(), packed_bake->meta_files_num);
407 transfer_to_bake(
408 packed_bake->blob_files, packed_data->blob_files.data(), packed_bake->blob_files_num);
409
410 /* Should have been freed before. */
411 BLI_assert(bake->packed == nullptr);
412 bake->packed = packed_bake;
413 }
414
415 /* Tag simulations as being baked. */
416 for (NodeBakeRequest &request : job.bake_requests) {
417 if (request.node_type != GEO_NODE_SIMULATION_OUTPUT) {
418 continue;
419 }
420 NodesModifierData &nmd = *request.nmd;
421 if (bake::SimulationNodeCache *node_cache = nmd.runtime->cache->get_simulation_node_cache(
422 request.bake_id))
423 {
424 if (!node_cache->bake.frames.is_empty()) {
425 /* Tag the caches as being baked so that they are not changed anymore. */
426 node_cache->cache_status = bake::CacheStatus::Baked;
427 }
428 }
430 }
431
432 job.scene->r.cfra = old_frame;
434
435 worker_status->progress = 1.0f;
436 worker_status->do_update = true;
437}
438
439static void bake_geometry_nodes_endjob(void *customdata)
440{
441 BakeGeometryNodesJob &job = *static_cast<BakeGeometryNodesJob *>(customdata);
442 WM_set_locked_interface(job.wm, false);
443 G.is_rendering = false;
447}
448
450{
452 &bake.data_blocks_num,
453 &bake.active_data_block,
454 [](NodesModifierDataBlock *data_block) {
455 nodes_modifier_data_block_destruct(data_block, true);
456 });
457}
458
460{
461 switch (request.node_type) {
463 if (bake::SimulationNodeCache *node_cache =
464 request.nmd->runtime->cache->get_simulation_node_cache(request.bake_id))
465 {
466 node_cache->reset();
467 }
468 break;
469 }
470 case GEO_NODE_BAKE: {
471 if (bake::BakeNodeCache *node_cache = request.nmd->runtime->cache->get_bake_node_cache(
472 request.bake_id))
473 {
474 node_cache->reset();
475 }
476 break;
477 }
478 }
479}
480
481static void try_delete_bake(
482 bContext *C, Object &object, NodesModifierData &nmd, const int bake_id, ReportList *reports)
483{
484 Main *bmain = CTX_data_main(C);
485 if (!nmd.runtime->cache) {
486 return;
487 }
488 bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
489 std::lock_guard lock{modifier_cache.mutex};
490 if (auto *node_cache = modifier_cache.simulation_cache_by_id.lookup_ptr(bake_id)) {
491 (*node_cache)->reset();
492 }
493 else if (auto *node_cache = modifier_cache.bake_cache_by_id.lookup_ptr(bake_id)) {
494 (*node_cache)->reset();
495 }
496 NodesModifierBake *bake = nmd.find_bake(bake_id);
497 if (!bake) {
498 return;
499 }
501
502 if (bake->packed) {
504 bake->packed = nullptr;
505 }
506
507 const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
508 *bmain, object, nmd, bake_id);
509 if (!bake_path) {
510 return;
511 }
512 const char *meta_dir = bake_path->meta_dir.c_str();
513 if (BLI_exists(meta_dir)) {
514 if (BLI_delete(meta_dir, true, true)) {
515 BKE_reportf(reports, RPT_ERROR, "Failed to remove metadata directory %s", meta_dir);
516 }
517 }
518 const char *blobs_dir = bake_path->blobs_dir.c_str();
519 if (BLI_exists(blobs_dir)) {
520 if (BLI_delete(blobs_dir, true, true)) {
521 BKE_reportf(reports, RPT_ERROR, "Failed to remove blobs directory %s", blobs_dir);
522 }
523 }
524 if (bake_path->bake_dir.has_value()) {
525 const char *zone_bake_dir = bake_path->bake_dir->c_str();
526 /* Try to delete zone bake directory if it is empty. */
527 BLI_delete(zone_bake_dir, true, false);
528 }
529 if (const std::optional<std::string> modifier_bake_dir = bake::get_modifier_bake_path(
530 *bmain, object, nmd))
531 {
532 /* Try to delete modifier bake directory if it is empty. */
533 BLI_delete(modifier_bake_dir->c_str(), true, false);
534 }
535}
536
547
550 wmOperator *op,
551 const BakeRequestsMode mode)
552{
553 for (NodeBakeRequest &request : requests) {
554 reset_old_bake_cache(request);
555 if (NodesModifierBake *bake = request.nmd->find_bake(request.bake_id)) {
557 }
558 try_delete_bake(C, *request.object, *request.nmd, request.bake_id, op->reports);
559 }
560
561 BakeGeometryNodesJob *job = MEM_new<BakeGeometryNodesJob>(__func__);
562 job->wm = CTX_wm_manager(C);
563 job->bmain = CTX_data_main(C);
565 job->scene = CTX_data_scene(C);
566 job->bake_requests = std::move(requests);
567 WM_set_locked_interface(job->wm, true);
568
569 if (mode == BakeRequestsMode::Sync) {
570 wmJobWorkerStatus worker_status{};
571 bake_geometry_nodes_startjob(job, &worker_status);
573 MEM_delete(job);
574 return OPERATOR_FINISHED;
575 }
576
577 wmJob *wm_job = WM_jobs_get(job->wm,
579 job->scene,
580 "Bake Nodes",
583
585 wm_job, job, [](void *job) { MEM_delete(static_cast<BakeGeometryNodesJob *>(job)); });
589
593}
594
596 Scene &scene,
597 const Span<Object *> objects)
598{
600 for (Object *object : objects) {
601 if (!BKE_id_is_editable(&bmain, &object->id)) {
602 continue;
603 }
604 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
605 if (md->type != eModifierType_Nodes) {
606 continue;
607 }
609 continue;
610 }
611 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
612 if (!nmd->node_group) {
613 continue;
614 }
615 if (!nmd->runtime->cache) {
616 continue;
617 }
618 for (const bNestedNodeRef &nested_node_ref : nmd->node_group->nested_node_refs_span()) {
619 const int id = nested_node_ref.id;
620 const bNode *node = nmd->node_group->find_nested_node(id);
622 continue;
623 }
624 NodeBakeRequest request;
625 request.object = object;
626 request.nmd = nmd;
627 request.bake_id = id;
628 request.node_type = node->type_legacy;
629 request.blob_sharing = std::make_unique<bake::BlobWriteSharing>();
631 request.path = bake::get_node_bake_path(bmain, *object, *nmd, id);
632 }
633 std::optional<IndexRange> frame_range = bake::get_node_bake_frame_range(
634 scene, *object, *nmd, id);
635 if (!frame_range) {
636 continue;
637 }
638 request.frame_start = frame_range->first();
639 request.frame_end = frame_range->last();
640
641 requests.append(std::move(request));
642 }
643 }
644 }
645 return requests;
646}
647
649{
650 Scene *scene = CTX_data_scene(C);
651 Main *bmain = CTX_data_main(C);
652
653 Vector<Object *> objects;
654 if (RNA_boolean_get(op->ptr, "selected")) {
655 CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
656 objects.append(object);
657 }
659 }
660 else {
661 if (Object *object = CTX_data_active_object(C)) {
662 objects.append(object);
663 }
664 }
665
666 return collect_simulations_to_bake(*bmain, *scene, objects);
667}
668
674
677 {
678 /* Normalize the paths so we can compare them. */
679 DynamicStackBuffer<256> norm_buf(s.size() + 1, 8);
680 memcpy(norm_buf.buffer(), s.data(), s.size() + 1);
681 char *norm = static_cast<char *>(norm_buf.buffer());
682
684
685 /* Strip ending slash. */
687
689 return get_default_hash(norm);
690 }
691};
692
694 bool operator()(const StringRef a, const StringRef b) const
695 {
696 return BLI_path_cmp_normalized(a.data(), b.data()) == 0;
697 }
698};
699
700static bool bake_directory_has_data(const StringRefNull absolute_bake_dir)
701{
702 char meta_dir[FILE_MAX];
703 BLI_path_join(meta_dir, sizeof(meta_dir), absolute_bake_dir.c_str(), "meta");
704 char blobs_dir[FILE_MAX];
705 BLI_path_join(blobs_dir, sizeof(blobs_dir), absolute_bake_dir.c_str(), "blobs");
706
707 if (!BLI_is_dir(meta_dir) || !BLI_is_dir(blobs_dir)) {
708 return false;
709 }
710
711 return true;
712}
713
715{
717 return true;
718 }
719 for (const NodesModifierBake &bake : Span{nmd.bakes, nmd.bakes_num}) {
720 if (bake.bake_target == NODES_MODIFIER_BAKE_TARGET_DISK) {
721 return true;
722 }
723 }
724 return false;
725}
726
728 Object &object,
730 wmOperator *op)
731{
732 const bool bake_directory_set = !StringRef(nmd.bake_directory).is_empty();
733 if (bake_directory_set) {
734 return;
735 }
736 if (!may_have_disk_bake(nmd)) {
737 return;
738 }
739
740 Main *bmain = CTX_data_main(C);
741
743 RPT_INFO,
744 "Bake directory of object %s, modifier %s is empty, setting default path",
745 object.id.name + 2,
746 nmd.modifier.name);
747
749 bake::get_default_modifier_bake_directory(*bmain, object, nmd).c_str());
750}
751
753 wmOperator *op,
754 const Span<Object *> objects)
755{
756 Main *bmain = CTX_data_main(C);
757
758 for (Object *object : objects) {
759 if (!BKE_id_is_editable(bmain, &object->id)) {
760 continue;
761 }
762
763 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
764 if (md->type != eModifierType_Nodes) {
765 continue;
766 }
767 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
769 }
770 }
771}
772
773/* Map for counting path references. */
774using PathUsersMap = Map<std::string,
775 int,
776 default_inline_buffer_capacity(sizeof(std::string)),
780
782{
783 Main *bmain = CTX_data_main(C);
784
785 PathUsersMap path_users;
786 for (const Object *object : objects) {
787 const char *base_path = ID_BLEND_PATH(bmain, &object->id);
788
789 LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) {
790 if (md->type != eModifierType_Nodes) {
791 continue;
792 }
793 const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
794 if (StringRef(nmd->bake_directory).is_empty()) {
795 continue;
796 }
797
798 char absolute_bake_dir[FILE_MAX];
799 STRNCPY(absolute_bake_dir, nmd->bake_directory);
800 BLI_path_abs(absolute_bake_dir, base_path);
801 path_users.add_or_modify(
802 absolute_bake_dir, [](int *value) { *value = 1; }, [](int *value) { ++(*value); });
803 }
804 }
805
806 return path_users;
807}
808
810 wmOperator *op,
811 const wmEvent * /*event*/)
812{
813 Vector<Object *> objects;
814 if (RNA_boolean_get(op->ptr, "selected")) {
815 CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
816 objects.append(object);
817 }
819 }
820 else {
821 if (Object *object = CTX_data_active_object(C)) {
822 objects.append(object);
823 }
824 }
825
826 /* Set empty paths to default if necessary. */
827 bake_simulation_validate_paths(C, op, objects);
828
829 PathUsersMap path_users = bake_simulation_get_path_users(C, objects);
830 bool has_path_conflict = false;
831 bool has_existing_bake_data = false;
832 for (const auto &item : path_users.items()) {
833 /* Check if multiple caches are writing to the same bake directory. */
834 if (item.value > 1) {
836 RPT_ERROR,
837 "Path conflict: %d caches set to path %s",
838 item.value,
839 item.key.data());
840 has_path_conflict = true;
841 }
842
843 /* Check if path exists and contains bake data already. */
844 if (bake_directory_has_data(item.key.data())) {
845 has_existing_bake_data = true;
846 }
847 }
848
849 if (has_path_conflict) {
851 return OPERATOR_CANCELLED;
852 }
853 if (has_existing_bake_data) {
855 op,
856 IFACE_("Overwrite existing bake data?"),
857 nullptr,
858 IFACE_("Bake"),
860 false);
861 }
863 return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Async);
864}
865
875
877{
878 Vector<Object *> objects;
879 if (RNA_boolean_get(op->ptr, "selected")) {
880 CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
881 objects.append(object);
882 }
884 }
885 else {
886 if (Object *object = CTX_data_active_object(C)) {
887 objects.append(object);
888 }
889 }
890
891 if (objects.is_empty()) {
892 return OPERATOR_CANCELLED;
893 }
894
895 for (Object *object : objects) {
896 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
897 if (md->type == eModifierType_Nodes) {
898 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
899 for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
900 try_delete_bake(C, *object, *nmd, bake.id, op->reports);
901 }
902 }
903 }
904
906 }
907
909
910 return OPERATOR_FINISHED;
911}
912
914{
915 Main *bmain = CTX_data_main(C);
916 Scene *scene = CTX_data_scene(C);
917 Object *object = reinterpret_cast<Object *>(
919 if (object == nullptr) {
920 return {};
921 }
922 char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
923 if (modifier_name == nullptr) {
924 return {};
925 }
927
929 if (md == nullptr) {
930 return {};
931 }
932 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
933 if (nmd.node_group == nullptr) {
934 return {};
935 }
937 BKE_report(op->reports, RPT_ERROR, "Modifier containing the node is disabled");
938 return {};
939 }
940
942
943 const int bake_id = RNA_int_get(op->ptr, "bake_id");
944 const bNode *node = nmd.node_group->find_nested_node(bake_id);
945 if (node == nullptr) {
946 return {};
947 }
949 return {};
950 }
951
952 NodeBakeRequest request;
953 request.object = object;
954 request.nmd = &nmd;
955 request.bake_id = bake_id;
956 request.node_type = node->type_legacy;
957 request.blob_sharing = std::make_unique<bake::BlobWriteSharing>();
958
959 const NodesModifierBake *bake = nmd.find_bake(bake_id);
960 if (!bake) {
961 return {};
962 }
963 if (bake::get_node_bake_target(*object, nmd, bake_id) == NODES_MODIFIER_BAKE_TARGET_DISK) {
964 request.path = bake::get_node_bake_path(*bmain, *object, nmd, bake_id);
965 if (!request.path) {
967 RPT_INFO,
968 "Can't determine bake location on disk. Falling back to packed bake.");
969 }
970 }
971
972 if (node->type_legacy == GEO_NODE_BAKE && bake->bake_mode == NODES_MODIFIER_BAKE_MODE_STILL) {
973 const int current_frame = scene->r.cfra;
974 request.frame_start = current_frame;
975 request.frame_end = current_frame;
976 /* Delete old bake because otherwise this wouldn't be a still frame bake. This is not done for
977 * other bakes to avoid loosing data when starting a bake. */
978 try_delete_bake(C, *object, nmd, bake_id, op->reports);
979 }
980 else {
981 const std::optional<IndexRange> frame_range = bake::get_node_bake_frame_range(
982 *scene, *object, nmd, bake_id);
983 if (!frame_range.has_value()) {
984 return {};
985 }
986 if (frame_range->is_empty()) {
987 return {};
988 }
989 request.frame_start = frame_range->first();
990 request.frame_end = frame_range->last();
991 }
992
994 requests.append(std::move(request));
995 return requests;
996}
997
999 wmOperator *op,
1000 const wmEvent * /*event*/)
1001{
1003 if (requests.is_empty()) {
1004 return OPERATOR_CANCELLED;
1005 }
1006 return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Async);
1007}
1008
1010{
1012 if (requests.is_empty()) {
1013 return OPERATOR_CANCELLED;
1014 }
1015 return start_bake_job(C, std::move(requests), op, BakeRequestsMode::Sync);
1016}
1017
1027
1029{
1030 Main *bmain = CTX_data_main(C);
1031 Object *object = reinterpret_cast<Object *>(
1033 if (object == nullptr) {
1034 return OPERATOR_CANCELLED;
1035 }
1036 char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
1037 if (modifier_name == nullptr) {
1038 return OPERATOR_CANCELLED;
1039 }
1041
1043 if (md == nullptr) {
1044 return OPERATOR_CANCELLED;
1045 }
1046 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
1047 const int bake_id = RNA_int_get(op->ptr, "bake_id");
1048
1049 try_delete_bake(C, *object, nmd, bake_id, op->reports);
1050
1053 WM_main_add_notifier(NC_NODE, nullptr);
1054 return OPERATOR_FINISHED;
1055}
1056
1058{
1059 Main *bmain = CTX_data_main(C);
1060 Object *object = reinterpret_cast<Object *>(
1062 if (object == nullptr) {
1063 return OPERATOR_CANCELLED;
1064 }
1065 char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
1066 if (modifier_name == nullptr) {
1067 return OPERATOR_CANCELLED;
1068 }
1070
1072 if (md == nullptr) {
1073 return OPERATOR_CANCELLED;
1074 }
1075 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
1076 const int bake_id = RNA_int_get(op->ptr, "bake_id");
1077
1078 const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
1079 *bmain, *object, nmd, bake_id);
1080 if (!bake_path) {
1081 return OPERATOR_CANCELLED;
1082 }
1083 NodesModifierBake *bake = nmd.find_bake(bake_id);
1084 if (!bake) {
1085 return OPERATOR_CANCELLED;
1086 }
1087
1088 bake::pack_geometry_nodes_bake(*bmain, op->reports, *object, nmd, *bake);
1089
1091 WM_main_add_notifier(NC_NODE, nullptr);
1092 return OPERATOR_FINISHED;
1093}
1094
1096 wmOperator *op,
1097 const wmEvent * /*event*/)
1098{
1099 uiPopupMenu *pup;
1100 uiLayout *layout;
1101
1102 pup = UI_popup_menu_begin(C, IFACE_("Unpack"), ICON_NONE);
1103 layout = UI_popup_menu_layout(pup);
1104
1106 uiItemsFullEnumO(layout,
1107 op->type->idname,
1108 "method",
1109 static_cast<IDProperty *>(op->ptr->data),
1111 UI_ITEM_NONE);
1112
1113 UI_popup_menu_end(C, pup);
1114
1115 return OPERATOR_INTERFACE;
1116}
1117
1119{
1120 Main *bmain = CTX_data_main(C);
1121 Object *object = reinterpret_cast<Object *>(
1123 if (object == nullptr) {
1124 return OPERATOR_CANCELLED;
1125 }
1126 char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
1127 if (modifier_name == nullptr) {
1128 return OPERATOR_CANCELLED;
1129 }
1131
1133 if (md == nullptr) {
1134 return OPERATOR_CANCELLED;
1135 }
1136 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
1137 const int bake_id = RNA_int_get(op->ptr, "bake_id");
1138 NodesModifierBake *bake = nmd.find_bake(bake_id);
1139 if (!bake) {
1140 return OPERATOR_CANCELLED;
1141 }
1142
1143 const ePF_FileStatus method = ePF_FileStatus(RNA_enum_get(op->ptr, "method"));
1144
1146 *bmain, op->reports, *object, nmd, *bake, method);
1148 return OPERATOR_CANCELLED;
1149 }
1150
1152 WM_main_add_notifier(NC_NODE, nullptr);
1153 return OPERATOR_FINISHED;
1154}
1155
1157{
1158 ot->name = "Calculate Simulation to Frame";
1159 ot->description =
1160 "Calculate simulations in geometry nodes modifiers from the start to current frame";
1161 ot->idname = __func__;
1162
1163 ot->invoke = simulate_to_frame_invoke;
1164 ot->modal = simulate_to_frame_modal;
1165 ot->poll = simulate_to_frame_poll;
1166
1167 RNA_def_boolean(ot->srna,
1168 "selected",
1169 false,
1170 "Selected",
1171 "Calculate all selected objects instead of just the active object");
1172}
1173
1175{
1176 ot->name = "Bake Simulation";
1177 ot->description = "Bake simulations in geometry nodes modifiers";
1178 ot->idname = __func__;
1179
1180 ot->exec = bake_simulation_exec;
1181 ot->invoke = bake_simulation_invoke;
1182 ot->modal = bake_simulation_modal;
1183 ot->poll = bake_simulation_poll;
1184
1185 RNA_def_boolean(ot->srna, "selected", false, "Selected", "Bake cache on all selected objects");
1186}
1187
1189{
1190 ot->name = "Delete Cached Simulation";
1191 ot->description = "Delete cached/baked simulations in geometry nodes modifiers";
1192 ot->idname = __func__;
1193
1196
1197 RNA_def_boolean(ot->srna, "selected", false, "Selected", "Delete cache on all selected objects");
1198}
1199
1201{
1203
1204 RNA_def_string(ot->srna,
1205 "modifier_name",
1206 nullptr,
1207 0,
1208 "Modifier Name",
1209 "Name of the modifier that contains the node");
1211 ot->srna, "bake_id", 0, 0, INT32_MAX, "Bake ID", "Nested node id of the node", 0, INT32_MAX);
1212}
1213
1215{
1216 ot->name = "Bake Geometry Node";
1217 ot->description = "Bake a single bake node or simulation";
1218 ot->idname = "OBJECT_OT_geometry_node_bake_single";
1219
1220 ot->invoke = bake_single_node_invoke;
1221 ot->exec = bake_single_node_exec;
1222 ot->modal = bake_single_node_modal;
1223
1225}
1226
1228{
1229 ot->name = "Delete Geometry Node Bake";
1230 ot->description = "Delete baked data of a single bake node or simulation";
1231 ot->idname = "OBJECT_OT_geometry_node_bake_delete_single";
1232
1234
1236}
1237
1239{
1240 ot->name = "Pack Geometry Node Bake";
1241 ot->description = "Pack baked data from disk into the .blend file";
1242 ot->idname = "OBJECT_OT_geometry_node_bake_pack_single";
1243
1244 ot->exec = pack_single_bake_exec;
1245
1247}
1248
1250{
1251 ot->name = "Unpack Geometry Node Bake";
1252 ot->description = "Unpack baked data from the .blend file to disk";
1253 ot->idname = "OBJECT_OT_geometry_node_bake_unpack_single";
1254
1256 ot->invoke = unpack_single_bake_invoke;
1257
1259
1260 static const EnumPropertyItem method_items[] = {
1261 {PF_USE_LOCAL,
1262 "USE_LOCAL",
1263 0,
1264 "Use bake from current directory (create when necessary)",
1265 ""},
1267 "WRITE_LOCAL",
1268 0,
1269 "Write bake to current directory (overwrite existing bake)",
1270 ""},
1272 "USE_ORIGINAL",
1273 0,
1274 "Use bake in original location (create when necessary)",
1275 ""},
1277 "WRITE_ORIGINAL",
1278 0,
1279 "Write bake to original location (overwrite existing file)",
1280 ""},
1281 {0, nullptr, 0, nullptr, nullptr},
1282 };
1283
1284 RNA_def_enum(ot->srna, "method", method_items, PF_USE_LOCAL, "Method", "How to unpack");
1285}
1286
1287} // 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:2503
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)
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
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2697
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:373
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:456
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:429
File and directory operations.
#define LISTBASE_FOREACH(type, var, list)
ATTR_WARN_UNUSED_RESULT const size_t num
#define BLI_SCOPED_DEFER(function_to_defer)
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:688
#define ELEM(...)
#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:982
@ 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
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
void uiItemsFullEnumO(uiLayout *layout, blender::StringRefNull opname, blender::StringRefNull propname, IDProperty *properties, wmOperatorCallContext context, eUI_Item_Flag flag, const int active=-1)
@ WM_JOB_TYPE_CALCULATE_SIMULATION_NODES
Definition WM_api.hh:1759
@ WM_JOB_TYPE_BAKE_GEOMETRY_NODES
Definition WM_api.hh:1760
@ WM_JOB_PROGRESS
Definition WM_api.hh:1716
#define NC_NODE
Definition WM_types.hh:391
#define ND_DISPLAY
Definition WM_types.hh:488
#define ND_MODIFIER
Definition WM_types.hh:459
ReportList * reports
Definition WM_types.hh:1025
@ WM_OP_EXEC_REGION_WIN
Definition WM_types.hh:246
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:245
#define ND_SPACE_VIEW3D
Definition WM_types.hh:525
#define NC_OBJECT
Definition WM_types.hh:376
#define NS_VIEW3D_SHADING
Definition WM_types.hh:574
#define NC_SPACE
Definition WM_types.hh:389
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
#define INT32_MAX
#define INT32_MIN
#define MEM_SAFE_FREE(v)
#define ID_BLEND_PATH(_bmain, _id)
static const char * modifier_name[LS_MODIFIER_NUM]
Definition linestyle.cc:679
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 void try_delete_bake(bContext *C, Object &object, NodesModifierData &nmd, const int bake_id, ReportList *reports)
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)
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)
int RNA_int_get(PointerRNA *ptr, const char *name)
char * RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len)
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
const char * idname
Definition WM_types.hh:1032
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
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)
void WM_set_locked_interface(wmWindowManager *wm, bool lock)
wmOperatorType * ot
Definition wm_files.cc:4226
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:353
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:456
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:190
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:365
bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type)
Definition wm_jobs.cc:224
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:337
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)