Geometry Nodes: support packing bakes into .blend files
Previously, it was only possible to bake to disk with geometry nodes. This patch adds support for storing the baked data directly in the .blend file. By default, new bakes are stored in the .blend file now. Whether a new bake should be packed or stored on disk can be configured in two places: in the properties of the bake node and in the bake panel of the modifier. These settings don't affect existing bakes, only the next bake. To unpack or pack an individual bake, there is a new operator button next to the bake button. The icon and the label below indicate where the bake is currently stored. The label now also contains the size of the bake. To unpack or pack all bakes, the `File > External Data > Pack Resources / Unpack Resources` operators can be used. The unpack operator also has a new title that mentions the number if individual files separate from the number of bakes. This works better than just listing a number of files because a bake can consist of many files. Pull Request: https://projects.blender.org/blender/blender/pulls/124230
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
#include "BKE_bake_items_paths.hh"
|
||||
#include "BKE_bake_items_serialize.hh"
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
struct NodesModifierData;
|
||||
struct Main;
|
||||
struct Object;
|
||||
@@ -35,8 +37,11 @@ enum class CacheStatus {
|
||||
struct FrameCache {
|
||||
SubFrame frame;
|
||||
BakeState state;
|
||||
/** Used when the baked data is loaded lazily. */
|
||||
std::optional<std::string> meta_path;
|
||||
/**
|
||||
* Used when the baked data is loaded lazily. The meta data either has to be loaded from a file
|
||||
* or from an in-memory buffer.
|
||||
*/
|
||||
std::optional<std::variant<std::string, Span<std::byte>>> meta_data_source;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -55,8 +60,11 @@ struct NodeBakeCache {
|
||||
/** All cached frames sorted by frame. */
|
||||
Vector<std::unique_ptr<FrameCache>> frames;
|
||||
|
||||
/** Where to load blobs from disk when loading the baked data lazily. */
|
||||
/** Loads blob data from memory when the bake is packed. */
|
||||
std::unique_ptr<MemoryBlobReader> memory_blob_reader;
|
||||
/** Where to load blobs from disk when loading the baked data lazily from disk. */
|
||||
std::optional<std::string> blobs_dir;
|
||||
|
||||
/** Used to avoid reading blobs multiple times for different frames. */
|
||||
std::unique_ptr<BlobReadSharing> blob_sharing;
|
||||
/** Used to avoid checking if a bake exists many times. */
|
||||
@@ -98,6 +106,8 @@ struct ModifierCache {
|
||||
SimulationNodeCache *get_simulation_node_cache(const int id);
|
||||
BakeNodeCache *get_bake_node_cache(const int id);
|
||||
NodeBakeCache *get_node_bake_cache(const int id);
|
||||
|
||||
void reset_cache(int id);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -106,6 +116,9 @@ struct ModifierCache {
|
||||
*/
|
||||
void scene_simulation_states_reset(Scene &scene);
|
||||
|
||||
std::optional<NodesModifierBakeTarget> get_node_bake_target(const Object &object,
|
||||
const NodesModifierData &nmd,
|
||||
int node_id);
|
||||
std::optional<BakePath> get_node_bake_path(const Main &bmain,
|
||||
const Object &object,
|
||||
const NodesModifierData &nmd,
|
||||
@@ -119,10 +132,14 @@ std::optional<std::string> get_modifier_bake_path(const Main &bmain,
|
||||
const NodesModifierData &nmd);
|
||||
|
||||
/**
|
||||
* Get the directory that contains all baked data for the given modifier by default.
|
||||
* Get default directory for baking modifier to disk.
|
||||
*/
|
||||
std::string get_default_modifier_bake_directory(const Main &bmain,
|
||||
const Object &object,
|
||||
const NodesModifierData &nmd);
|
||||
std::string get_default_node_bake_directory(const Main &bmain,
|
||||
const Object &object,
|
||||
const NodesModifierData &nmd,
|
||||
int node_id);
|
||||
|
||||
} // namespace blender::bke::bake
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#include "BKE_bake_items_paths.hh"
|
||||
#include "BKE_packedFile.hh"
|
||||
|
||||
struct ReportList;
|
||||
struct Main;
|
||||
|
||||
namespace blender::bke::bake {
|
||||
|
||||
NodesModifierPackedBake *pack_bake_from_disk(const BakePath &bake_path, ReportList *reports);
|
||||
|
||||
[[nodiscard]] bool unpack_bake_to_disk(const NodesModifierPackedBake &packed_bake,
|
||||
const BakePath &bake_path,
|
||||
ReportList *reports);
|
||||
|
||||
enum class PackGeometryNodesBakeResult {
|
||||
NoDataFound,
|
||||
PackedAlready,
|
||||
Success,
|
||||
};
|
||||
|
||||
PackGeometryNodesBakeResult pack_geometry_nodes_bake(Main &bmain,
|
||||
ReportList *reports,
|
||||
Object &object,
|
||||
NodesModifierData &nmd,
|
||||
NodesModifierBake &bake);
|
||||
|
||||
enum class UnpackGeometryNodesBakeResult {
|
||||
BlendFileNotSaved,
|
||||
NoPackedData,
|
||||
Error,
|
||||
Success,
|
||||
};
|
||||
|
||||
UnpackGeometryNodesBakeResult unpack_geometry_nodes_bake(Main &bmain,
|
||||
ReportList *reports,
|
||||
Object &object,
|
||||
NodesModifierData &nmd,
|
||||
NodesModifierBake &bake,
|
||||
ePF_FileStatus how);
|
||||
|
||||
} // namespace blender::bke::bake
|
||||
@@ -36,7 +36,7 @@ struct BakePath {
|
||||
};
|
||||
|
||||
std::string frame_to_file_name(const SubFrame &frame);
|
||||
std::optional<SubFrame> file_name_to_frame(const StringRefNull file_name);
|
||||
std::optional<SubFrame> file_name_to_frame(const StringRef file_name);
|
||||
|
||||
Vector<MetaFile> find_sorted_meta_files(const StringRefNull meta_dir);
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@ class BlobReader {
|
||||
* Abstract base class for writing binary data.
|
||||
*/
|
||||
class BlobWriter {
|
||||
protected:
|
||||
int64_t total_written_size_ = 0;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Write the provided binary data.
|
||||
@@ -62,6 +65,11 @@ class BlobWriter {
|
||||
*/
|
||||
virtual BlobSlice write_as_stream(StringRef file_extension,
|
||||
FunctionRef<void(std::ostream &)> fn);
|
||||
|
||||
int64_t written_size() const
|
||||
{
|
||||
return total_written_size_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -184,6 +192,49 @@ class DiskBlobWriter : public BlobWriter {
|
||||
FunctionRef<void(std::ostream &)> fn) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* A specific #BlobWriter that keeps all data in memory.
|
||||
*/
|
||||
class MemoryBlobWriter : public BlobWriter {
|
||||
public:
|
||||
struct OutputStream {
|
||||
std::unique_ptr<std::ostringstream> stream;
|
||||
int64_t offset = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
std::string base_name_;
|
||||
std::string blob_name_;
|
||||
Map<std::string, OutputStream> stream_by_name_;
|
||||
int independent_file_count_ = 0;
|
||||
|
||||
public:
|
||||
MemoryBlobWriter(std::string base_name);
|
||||
|
||||
BlobSlice write(const void *data, int64_t size) override;
|
||||
|
||||
BlobSlice write_as_stream(StringRef file_extension,
|
||||
FunctionRef<void(std::ostream &)> fn) override;
|
||||
|
||||
const Map<std::string, OutputStream> &get_stream_by_name() const
|
||||
{
|
||||
return stream_by_name_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A specific #BlobReader that reads data from in-memory buffers.
|
||||
*/
|
||||
class MemoryBlobReader : public BlobReader {
|
||||
private:
|
||||
Map<StringRef, Span<std::byte>> blob_by_name_;
|
||||
|
||||
public:
|
||||
void add(StringRef name, Span<std::byte> blob);
|
||||
|
||||
[[nodiscard]] bool read(const BlobSlice &slice, void *r_data) const override;
|
||||
};
|
||||
|
||||
void serialize_bake(const BakeState &bake_state,
|
||||
BlobWriter &blob_writer,
|
||||
BlobWriteSharing &blob_sharing,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "BLI_implicit_sharing.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#define RET_OK 0
|
||||
@@ -46,7 +47,8 @@ PackedFile *BKE_packedfile_duplicate(const PackedFile *pf_src);
|
||||
PackedFile *BKE_packedfile_new(ReportList *reports,
|
||||
const char *filepath_rel,
|
||||
const char *basepath);
|
||||
PackedFile *BKE_packedfile_new_from_memory(void *mem, int memlen);
|
||||
PackedFile *BKE_packedfile_new_from_memory(
|
||||
const void *mem, int memlen, const blender::ImplicitSharingInfo *sharing_info = nullptr);
|
||||
|
||||
/**
|
||||
* No libraries for now.
|
||||
@@ -108,7 +110,20 @@ void BKE_packedfile_free(PackedFile *pf);
|
||||
|
||||
/* Info. */
|
||||
|
||||
int BKE_packedfile_count_all(Main *bmain);
|
||||
struct PackedFileCount {
|
||||
/** Counts e.g. packed images and sounds. */
|
||||
int individual_files = 0;
|
||||
/** Counts bakes that may consist of multiple files. */
|
||||
int bakes = 0;
|
||||
|
||||
int total() const
|
||||
{
|
||||
return this->individual_files + this->bakes;
|
||||
}
|
||||
};
|
||||
|
||||
PackedFileCount BKE_packedfile_count_all(Main *bmain);
|
||||
|
||||
/**
|
||||
* This function compares a packed file to a 'real' file.
|
||||
* It returns an integer indicating if:
|
||||
|
||||
@@ -69,6 +69,7 @@ set(SRC
|
||||
intern/autoexec.cc
|
||||
intern/bake_data_block_map.cc
|
||||
intern/bake_geometry_nodes_modifier.cc
|
||||
intern/bake_geometry_nodes_modifier_pack.cc
|
||||
intern/bake_items.cc
|
||||
intern/bake_items_paths.cc
|
||||
intern/bake_items_serialize.cc
|
||||
@@ -340,6 +341,7 @@ set(SRC
|
||||
BKE_bake_data_block_id.hh
|
||||
BKE_bake_data_block_map.hh
|
||||
BKE_bake_geometry_nodes_modifier.hh
|
||||
BKE_bake_geometry_nodes_modifier_pack.hh
|
||||
BKE_bake_items.hh
|
||||
BKE_bake_items_paths.hh
|
||||
BKE_bake_items_serialize.hh
|
||||
|
||||
@@ -71,6 +71,16 @@ NodeBakeCache *ModifierCache::get_node_bake_cache(const int id)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ModifierCache::reset_cache(const int id)
|
||||
{
|
||||
if (SimulationNodeCache *cache = this->get_simulation_node_cache(id)) {
|
||||
cache->reset();
|
||||
}
|
||||
if (BakeNodeCache *cache = this->get_bake_node_cache(id)) {
|
||||
cache->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void scene_simulation_states_reset(Scene &scene)
|
||||
{
|
||||
FOREACH_SCENE_OBJECT_BEGIN (&scene, ob) {
|
||||
@@ -97,6 +107,9 @@ std::optional<std::string> get_modifier_bake_path(const Main &bmain,
|
||||
if (StringRef(nmd.bake_directory).is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!BLI_path_is_rel(nmd.bake_directory)) {
|
||||
return nmd.bake_directory;
|
||||
}
|
||||
const char *base_path = ID_BLEND_PATH(&bmain, &object.id);
|
||||
if (StringRef(base_path).is_empty()) {
|
||||
return std::nullopt;
|
||||
@@ -107,6 +120,23 @@ std::optional<std::string> get_modifier_bake_path(const Main &bmain,
|
||||
return absolute_bake_dir;
|
||||
}
|
||||
|
||||
std::optional<NodesModifierBakeTarget> get_node_bake_target(const Object & /*object*/,
|
||||
const NodesModifierData &nmd,
|
||||
int node_id)
|
||||
{
|
||||
const NodesModifierBake *bake = nmd.find_bake(node_id);
|
||||
if (!bake) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (bake->bake_target != NODES_MODIFIER_BAKE_TARGET_INHERIT) {
|
||||
return NodesModifierBakeTarget(bake->bake_target);
|
||||
}
|
||||
if (nmd.bake_target != NODES_MODIFIER_BAKE_TARGET_INHERIT) {
|
||||
return NodesModifierBakeTarget(nmd.bake_target);
|
||||
}
|
||||
return NODES_MODIFIER_BAKE_TARGET_PACKED;
|
||||
}
|
||||
|
||||
std::optional<bake::BakePath> get_node_bake_path(const Main &bmain,
|
||||
const Object &object,
|
||||
const NodesModifierData &nmd,
|
||||
@@ -120,6 +150,9 @@ std::optional<bake::BakePath> get_node_bake_path(const Main &bmain,
|
||||
if (StringRef(bake->directory).is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!BLI_path_is_rel(bake->directory)) {
|
||||
return BakePath::from_single_root(bake->directory);
|
||||
}
|
||||
const char *base_path = ID_BLEND_PATH(&bmain, &object.id);
|
||||
if (StringRef(base_path).is_empty()) {
|
||||
return std::nullopt;
|
||||
@@ -217,4 +250,19 @@ std::string get_default_modifier_bake_directory(const Main &bmain,
|
||||
return dir;
|
||||
}
|
||||
|
||||
std::string get_default_node_bake_directory(const Main &bmain,
|
||||
const Object &object,
|
||||
const NodesModifierData &nmd,
|
||||
int node_id)
|
||||
{
|
||||
char dir[FILE_MAX];
|
||||
BLI_path_join(dir,
|
||||
sizeof(dir),
|
||||
"//",
|
||||
get_blend_file_name(bmain).c_str(),
|
||||
get_modifier_directory_name(object, nmd.modifier).c_str(),
|
||||
std::to_string(node_id).c_str());
|
||||
return dir;
|
||||
}
|
||||
|
||||
} // namespace blender::bke::bake
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_bake_geometry_nodes_modifier.hh"
|
||||
#include "BKE_bake_geometry_nodes_modifier_pack.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_packedFile.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#include "MOD_nodes.hh"
|
||||
|
||||
#include "BLI_fileops.hh"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
namespace blender::bke::bake {
|
||||
|
||||
static Vector<NodesModifierBakeFile> pack_files_from_directory(const StringRefNull directory,
|
||||
ReportList *reports)
|
||||
{
|
||||
if (!BLI_is_dir(directory.c_str())) {
|
||||
BKE_reportf(reports, RPT_ERROR, "%s is no directory", directory.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
direntry *dir_entries = nullptr;
|
||||
const int dir_entries_num = BLI_filelist_dir_contents(directory.c_str(), &dir_entries);
|
||||
BLI_SCOPED_DEFER([&]() { BLI_filelist_free(dir_entries, dir_entries_num); });
|
||||
|
||||
Vector<NodesModifierBakeFile> bake_files;
|
||||
for (const int i : IndexRange(dir_entries_num)) {
|
||||
const direntry &dir_entry = dir_entries[i];
|
||||
const StringRefNull dir_entry_path = dir_entry.path;
|
||||
const StringRefNull name = dir_entry.relname;
|
||||
NodesModifierBakeFile bake_file;
|
||||
bake_file.name = BLI_strdup_null(name.c_str());
|
||||
bake_file.packed_file = BKE_packedfile_new(reports, dir_entry_path.c_str(), "");
|
||||
if (bake_file.packed_file) {
|
||||
bake_files.append(bake_file);
|
||||
}
|
||||
}
|
||||
|
||||
return bake_files;
|
||||
}
|
||||
|
||||
NodesModifierPackedBake *pack_bake_from_disk(const BakePath &bake_path, ReportList *reports)
|
||||
{
|
||||
const Vector<NodesModifierBakeFile> meta_bake_files = pack_files_from_directory(
|
||||
bake_path.meta_dir, reports);
|
||||
if (meta_bake_files.is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Vector<NodesModifierBakeFile> blob_bake_files = pack_files_from_directory(
|
||||
bake_path.blobs_dir, reports);
|
||||
|
||||
NodesModifierPackedBake *packed_bake = MEM_cnew<NodesModifierPackedBake>(__func__);
|
||||
packed_bake->meta_files_num = meta_bake_files.size();
|
||||
packed_bake->blob_files_num = blob_bake_files.size();
|
||||
|
||||
packed_bake->meta_files = MEM_cnew_array<NodesModifierBakeFile>(packed_bake->meta_files_num,
|
||||
__func__);
|
||||
packed_bake->blob_files = MEM_cnew_array<NodesModifierBakeFile>(packed_bake->blob_files_num,
|
||||
__func__);
|
||||
|
||||
uninitialized_copy_n(meta_bake_files.data(), meta_bake_files.size(), packed_bake->meta_files);
|
||||
uninitialized_copy_n(blob_bake_files.data(), blob_bake_files.size(), packed_bake->blob_files);
|
||||
|
||||
return packed_bake;
|
||||
}
|
||||
|
||||
bool unpack_bake_to_disk(const NodesModifierPackedBake &packed_bake,
|
||||
const BakePath &bake_path,
|
||||
ReportList *reports)
|
||||
{
|
||||
auto unpack_file = [&](const StringRefNull directory, const NodesModifierBakeFile &bake_file) {
|
||||
char file_path[FILE_MAX];
|
||||
BLI_path_join(file_path, sizeof(file_path), directory.c_str(), bake_file.name);
|
||||
if (!BLI_file_ensure_parent_dir_exists(file_path)) {
|
||||
BKE_reportf(reports, RPT_ERROR, "Can't ensure directory: %s", directory.c_str());
|
||||
return false;
|
||||
}
|
||||
fstream fs(file_path, std::ios::out);
|
||||
fs.write(static_cast<const char *>(bake_file.packed_file->data), bake_file.packed_file->size);
|
||||
if (fs.bad()) {
|
||||
BKE_reportf(reports, RPT_ERROR, "Can't write file : %s", file_path);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const NodesModifierBakeFile &bake_file :
|
||||
Span{packed_bake.meta_files, packed_bake.meta_files_num})
|
||||
{
|
||||
if (!unpack_file(bake_path.meta_dir, bake_file)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const NodesModifierBakeFile &bake_file :
|
||||
Span{packed_bake.blob_files, packed_bake.blob_files_num})
|
||||
{
|
||||
if (!unpack_file(bake_path.blobs_dir, bake_file)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PackGeometryNodesBakeResult pack_geometry_nodes_bake(Main &bmain,
|
||||
ReportList *reports,
|
||||
Object &object,
|
||||
NodesModifierData &nmd,
|
||||
NodesModifierBake &bake)
|
||||
{
|
||||
if (bake.packed) {
|
||||
return PackGeometryNodesBakeResult::PackedAlready;
|
||||
}
|
||||
const std::optional<bake::BakePath> bake_path = get_node_bake_path(bmain, object, nmd, bake.id);
|
||||
if (!bake_path) {
|
||||
return PackGeometryNodesBakeResult::NoDataFound;
|
||||
}
|
||||
bake.packed = bake::pack_bake_from_disk(*bake_path, reports);
|
||||
if (!bake.packed) {
|
||||
return PackGeometryNodesBakeResult::NoDataFound;
|
||||
}
|
||||
nmd.runtime->cache->reset_cache(bake.id);
|
||||
bake.bake_target = NODES_MODIFIER_BAKE_TARGET_PACKED;
|
||||
DEG_id_tag_update(&object.id, ID_RECALC_GEOMETRY);
|
||||
return PackGeometryNodesBakeResult::Success;
|
||||
}
|
||||
|
||||
static bool directory_is_empty(const blender::StringRefNull path)
|
||||
{
|
||||
direntry *entries = nullptr;
|
||||
const int entries_num = BLI_filelist_dir_contents(path.c_str(), &entries);
|
||||
BLI_filelist_free(entries, entries_num);
|
||||
return entries_num == 0;
|
||||
}
|
||||
|
||||
static bool disk_bake_exists(const blender::bke::bake::BakePath &path)
|
||||
{
|
||||
return !directory_is_empty(path.meta_dir);
|
||||
}
|
||||
|
||||
UnpackGeometryNodesBakeResult unpack_geometry_nodes_bake(Main &bmain,
|
||||
ReportList *reports,
|
||||
Object &object,
|
||||
NodesModifierData &nmd,
|
||||
NodesModifierBake &bake,
|
||||
ePF_FileStatus how)
|
||||
{
|
||||
if (!bake.packed) {
|
||||
return UnpackGeometryNodesBakeResult::NoPackedData;
|
||||
}
|
||||
if (StringRef(BKE_main_blendfile_path(&bmain)).is_empty()) {
|
||||
BKE_report(reports, RPT_ERROR, "Can only unpack bake if the current .blend file is saved");
|
||||
return UnpackGeometryNodesBakeResult::BlendFileNotSaved;
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&object.id, ID_RECALC_GEOMETRY);
|
||||
|
||||
auto prepare_local_path = [&]() {
|
||||
const std::string directory = bake::get_default_node_bake_directory(
|
||||
bmain, object, nmd, bake.id);
|
||||
bake.flag |= NODES_MODIFIER_BAKE_CUSTOM_PATH;
|
||||
MEM_SAFE_FREE(bake.directory);
|
||||
bake.directory = BLI_strdup(directory.c_str());
|
||||
const char *base_path = ID_BLEND_PATH(&bmain, &object.id);
|
||||
char absolute_dir[FILE_MAX];
|
||||
STRNCPY(absolute_dir, directory.c_str());
|
||||
BLI_path_abs(absolute_dir, base_path);
|
||||
return bake::BakePath::from_single_root(absolute_dir);
|
||||
};
|
||||
auto prepare_original_path = [&]() {
|
||||
if (const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
|
||||
bmain, object, nmd, bake.id))
|
||||
{
|
||||
return *bake_path;
|
||||
}
|
||||
return prepare_local_path();
|
||||
};
|
||||
auto delete_bake_on_disk = [&](const bake::BakePath &bake_path) {
|
||||
BLI_delete(bake_path.meta_dir.c_str(), true, true);
|
||||
BLI_delete(bake_path.blobs_dir.c_str(), true, true);
|
||||
};
|
||||
auto free_packed_bake = [&]() {
|
||||
blender::nodes_modifier_packed_bake_free(bake.packed);
|
||||
bake.packed = nullptr;
|
||||
nmd.runtime->cache->reset_cache(bake.id);
|
||||
};
|
||||
auto finalize_on_success = [&]() {
|
||||
bake.bake_target = NODES_MODIFIER_BAKE_TARGET_DISK;
|
||||
return UnpackGeometryNodesBakeResult::Success;
|
||||
};
|
||||
|
||||
switch (how) {
|
||||
case PF_USE_ORIGINAL: {
|
||||
const bake::BakePath bake_path = prepare_original_path();
|
||||
if (!disk_bake_exists(bake_path)) {
|
||||
delete_bake_on_disk(bake_path);
|
||||
if (!bake::unpack_bake_to_disk(*bake.packed, bake_path, reports)) {
|
||||
return UnpackGeometryNodesBakeResult::Error;
|
||||
}
|
||||
}
|
||||
free_packed_bake();
|
||||
return finalize_on_success();
|
||||
}
|
||||
case PF_WRITE_ORIGINAL: {
|
||||
const bake::BakePath bake_path = prepare_original_path();
|
||||
delete_bake_on_disk(bake_path);
|
||||
if (!bake::unpack_bake_to_disk(*bake.packed, bake_path, reports)) {
|
||||
return UnpackGeometryNodesBakeResult::Error;
|
||||
}
|
||||
free_packed_bake();
|
||||
return finalize_on_success();
|
||||
}
|
||||
case PF_USE_LOCAL: {
|
||||
const bake::BakePath bake_path = prepare_local_path();
|
||||
if (!disk_bake_exists(bake_path)) {
|
||||
delete_bake_on_disk(bake_path);
|
||||
if (!bake::unpack_bake_to_disk(*bake.packed, bake_path, reports)) {
|
||||
return UnpackGeometryNodesBakeResult::Error;
|
||||
}
|
||||
}
|
||||
free_packed_bake();
|
||||
return finalize_on_success();
|
||||
}
|
||||
case PF_WRITE_LOCAL: {
|
||||
const bake::BakePath bake_path = prepare_local_path();
|
||||
delete_bake_on_disk(bake_path);
|
||||
if (!bake::unpack_bake_to_disk(*bake.packed, bake_path, reports)) {
|
||||
return UnpackGeometryNodesBakeResult::Error;
|
||||
}
|
||||
free_packed_bake();
|
||||
return finalize_on_success();
|
||||
}
|
||||
case PF_KEEP: {
|
||||
return finalize_on_success();
|
||||
}
|
||||
case PF_REMOVE: {
|
||||
free_packed_bake();
|
||||
return finalize_on_success();
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return UnpackGeometryNodesBakeResult::Error;
|
||||
}
|
||||
|
||||
} // namespace blender::bke::bake
|
||||
@@ -19,13 +19,18 @@ std::string frame_to_file_name(const SubFrame &frame)
|
||||
return file_name_c;
|
||||
}
|
||||
|
||||
std::optional<SubFrame> file_name_to_frame(const StringRefNull file_name)
|
||||
std::optional<SubFrame> file_name_to_frame(const StringRef file_name)
|
||||
{
|
||||
char modified_file_name[FILE_MAX];
|
||||
STRNCPY(modified_file_name, file_name.c_str());
|
||||
file_name.copy(modified_file_name);
|
||||
BLI_string_replace_char(modified_file_name, '_', '.');
|
||||
const SubFrame frame = std::stof(modified_file_name);
|
||||
return frame;
|
||||
try {
|
||||
const SubFrame frame = std::stof(modified_file_name);
|
||||
return frame;
|
||||
}
|
||||
catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<MetaFile> find_sorted_meta_files(const StringRefNull meta_dir)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "BLI_path_util.h"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
@@ -126,16 +127,24 @@ BlobSlice DiskBlobWriter::write(const void *data, const int64_t size)
|
||||
const int64_t old_offset = current_offset_;
|
||||
blob_stream_.write(static_cast<const char *>(data), size);
|
||||
current_offset_ += size;
|
||||
total_written_size_ += size;
|
||||
return {blob_name_, {old_offset, size}};
|
||||
}
|
||||
|
||||
static std::string make_independent_file_name(const StringRef base_name,
|
||||
const int file_index,
|
||||
const StringRef extension)
|
||||
{
|
||||
return fmt::format("{}_file_{}{}", base_name, file_index, extension);
|
||||
}
|
||||
|
||||
BlobSlice DiskBlobWriter::write_as_stream(const StringRef file_extension,
|
||||
const FunctionRef<void(std::ostream &)> fn)
|
||||
{
|
||||
BLI_assert(file_extension.startswith("."));
|
||||
independent_file_count_++;
|
||||
const std::string file_name = fmt::format(
|
||||
"{}_file_{}{}", base_name_, independent_file_count_, file_extension);
|
||||
const std::string file_name = make_independent_file_name(
|
||||
base_name_, independent_file_count_, file_extension);
|
||||
|
||||
char path[FILE_MAX];
|
||||
BLI_path_join(path, sizeof(path), blob_dir_.c_str(), file_name.c_str());
|
||||
@@ -143,9 +152,60 @@ BlobSlice DiskBlobWriter::write_as_stream(const StringRef file_extension,
|
||||
std::fstream stream{path, std::ios::out | std::ios::binary};
|
||||
fn(stream);
|
||||
const int64_t written_bytes_num = stream.tellg();
|
||||
total_written_size_ += written_bytes_num;
|
||||
return {file_name, {0, written_bytes_num}};
|
||||
}
|
||||
|
||||
void MemoryBlobReader::add(const StringRef name, const Span<std::byte> blob)
|
||||
{
|
||||
blob_by_name_.add(name, blob);
|
||||
}
|
||||
|
||||
bool MemoryBlobReader::read(const BlobSlice &slice, void *r_data) const
|
||||
{
|
||||
if (slice.range.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
const Span<std::byte> blob_data = blob_by_name_.lookup_default(slice.name, {});
|
||||
if (!blob_data.index_range().contains(slice.range)) {
|
||||
return false;
|
||||
}
|
||||
const void *copy_src = blob_data.slice(slice.range).data();
|
||||
memcpy(r_data, copy_src, slice.range.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
MemoryBlobWriter::MemoryBlobWriter(std::string base_name) : base_name_(std::move(base_name))
|
||||
{
|
||||
blob_name_ = base_name_ + ".blob";
|
||||
stream_by_name_.add(blob_name_, {std::make_unique<std::ostringstream>(std::ios::binary)});
|
||||
}
|
||||
|
||||
BlobSlice MemoryBlobWriter::write(const void *data, int64_t size)
|
||||
{
|
||||
OutputStream &stream = stream_by_name_.lookup(blob_name_);
|
||||
const int64_t old_offset = stream.offset;
|
||||
stream.stream->write(static_cast<const char *>(data), size);
|
||||
stream.offset += size;
|
||||
total_written_size_ += size;
|
||||
return {blob_name_, IndexRange::from_begin_size(old_offset, size)};
|
||||
}
|
||||
|
||||
BlobSlice MemoryBlobWriter::write_as_stream(const StringRef file_extension,
|
||||
const FunctionRef<void(std::ostream &)> fn)
|
||||
{
|
||||
BLI_assert(file_extension.startswith("."));
|
||||
independent_file_count_++;
|
||||
const std::string name = make_independent_file_name(
|
||||
base_name_, independent_file_count_, file_extension);
|
||||
OutputStream stream{std::make_unique<std::ostringstream>(std::ios::binary)};
|
||||
fn(*stream.stream);
|
||||
const int64_t size = stream.stream->tellp();
|
||||
stream_by_name_.add_new(name, std::move(stream));
|
||||
total_written_size_ += size;
|
||||
return {base_name_, IndexRange(size)};
|
||||
}
|
||||
|
||||
BlobWriteSharing::~BlobWriteSharing()
|
||||
{
|
||||
for (const ImplicitSharingInfo *sharing_info : stored_by_runtime_.keys()) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_packedFile_types.h"
|
||||
#include "DNA_sound_types.h"
|
||||
#include "DNA_vfont_types.h"
|
||||
@@ -28,6 +29,8 @@
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_bake_geometry_nodes_modifier.hh"
|
||||
#include "BKE_bake_geometry_nodes_modifier_pack.hh"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_image_format.h"
|
||||
#include "BKE_main.hh"
|
||||
@@ -37,9 +40,13 @@
|
||||
#include "BKE_vfont.hh"
|
||||
#include "BKE_volume.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "MOD_nodes.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
@@ -108,26 +115,27 @@ int BKE_packedfile_read(PackedFile *pf, void *data, int size)
|
||||
return size;
|
||||
}
|
||||
|
||||
int BKE_packedfile_count_all(Main *bmain)
|
||||
PackedFileCount BKE_packedfile_count_all(Main *bmain)
|
||||
{
|
||||
Image *ima;
|
||||
VFont *vf;
|
||||
bSound *sound;
|
||||
Volume *volume;
|
||||
int count = 0;
|
||||
|
||||
PackedFileCount count;
|
||||
|
||||
/* let's check if there are packed files... */
|
||||
for (ima = static_cast<Image *>(bmain->images.first); ima;
|
||||
ima = static_cast<Image *>(ima->id.next))
|
||||
{
|
||||
if (BKE_image_has_packedfile(ima) && !ID_IS_LINKED(ima)) {
|
||||
count++;
|
||||
count.individual_files++;
|
||||
}
|
||||
}
|
||||
|
||||
for (vf = static_cast<VFont *>(bmain->fonts.first); vf; vf = static_cast<VFont *>(vf->id.next)) {
|
||||
if (vf->packedfile && !ID_IS_LINKED(vf)) {
|
||||
count++;
|
||||
count.individual_files++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +143,7 @@ int BKE_packedfile_count_all(Main *bmain)
|
||||
sound = static_cast<bSound *>(sound->id.next))
|
||||
{
|
||||
if (sound->packedfile && !ID_IS_LINKED(sound)) {
|
||||
count++;
|
||||
count.individual_files++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +151,23 @@ int BKE_packedfile_count_all(Main *bmain)
|
||||
volume = static_cast<Volume *>(volume->id.next))
|
||||
{
|
||||
if (volume->packedfile && !ID_IS_LINKED(volume)) {
|
||||
count++;
|
||||
count.individual_files++;
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
|
||||
if (ID_IS_LINKED(object)) {
|
||||
continue;
|
||||
}
|
||||
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
|
||||
if (md->type == eModifierType_Nodes) {
|
||||
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
|
||||
for (const NodesModifierBake &bake : blender::Span{nmd->bakes, nmd->bakes_num}) {
|
||||
if (bake.packed) {
|
||||
count.bakes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,14 +201,20 @@ PackedFile *BKE_packedfile_duplicate(const PackedFile *pf_src)
|
||||
return pf_dst;
|
||||
}
|
||||
|
||||
PackedFile *BKE_packedfile_new_from_memory(void *mem, int memlen)
|
||||
PackedFile *BKE_packedfile_new_from_memory(const void *mem,
|
||||
int memlen,
|
||||
const blender::ImplicitSharingInfo *sharing_info)
|
||||
{
|
||||
BLI_assert(mem != nullptr);
|
||||
if (!sharing_info) {
|
||||
/* Assume we are the only owner of that memory currently. */
|
||||
sharing_info = blender::implicit_sharing::info_for_mem_free(const_cast<void *>(mem));
|
||||
}
|
||||
|
||||
PackedFile *pf = static_cast<PackedFile *>(MEM_callocN(sizeof(*pf), "PackedFile"));
|
||||
pf->data = mem;
|
||||
pf->size = memlen;
|
||||
pf->sharing_info = blender::implicit_sharing::info_for_mem_free(mem);
|
||||
pf->sharing_info = sharing_info;
|
||||
|
||||
return pf;
|
||||
}
|
||||
@@ -299,6 +329,20 @@ void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
|
||||
if (ID_IS_LINKED(object)) {
|
||||
continue;
|
||||
}
|
||||
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
|
||||
if (md->type == eModifierType_Nodes) {
|
||||
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
|
||||
for (NodesModifierBake &bake : blender::MutableSpan{nmd->bakes, nmd->bakes_num}) {
|
||||
blender::bke::bake::pack_geometry_nodes_bake(*bmain, reports, *object, *nmd, bake);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tot > 0) {
|
||||
BKE_reportf(reports, RPT_INFO, "Packed %d file(s)", tot);
|
||||
}
|
||||
@@ -816,6 +860,21 @@ void BKE_packedfile_unpack_all(Main *bmain, ReportList *reports, enum ePF_FileSt
|
||||
BKE_packedfile_unpack_volume(bmain, reports, volume, how);
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
|
||||
if (ID_IS_LINKED(object)) {
|
||||
continue;
|
||||
}
|
||||
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
|
||||
if (md->type == eModifierType_Nodes) {
|
||||
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
|
||||
for (NodesModifierBake &bake : blender::MutableSpan{nmd->bakes, nmd->bakes_num}) {
|
||||
blender::bke::bake::unpack_geometry_nodes_bake(
|
||||
*bmain, reports, *object, *nmd, bake, how);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BKE_packedfile_id_check(const ID *id)
|
||||
|
||||
@@ -249,6 +249,23 @@ class IndexRange {
|
||||
return value >= start_ && value < start_ + size_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when all indices in the given range are also in the current range.
|
||||
*/
|
||||
constexpr bool contains(const IndexRange range) const
|
||||
{
|
||||
if (range.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
if (range.start_ < start_) {
|
||||
return false;
|
||||
}
|
||||
if (range.start_ + range.size_ > start_ + size_) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new range, that contains a sub-interval of the current one.
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <cmath>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_struct_equality_utils.hh"
|
||||
|
||||
@@ -59,6 +60,11 @@ struct SubFrame {
|
||||
return {INT32_MAX, std::nexttowardf(1.0f, 0.0)};
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return get_default_hash(frame_, subframe_);
|
||||
}
|
||||
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_2(SubFrame, frame_, subframe_)
|
||||
|
||||
friend bool operator<(const SubFrame &a, const SubFrame &b)
|
||||
|
||||
@@ -4625,6 +4625,21 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
add_bevel_modifier_attribute_name_defaults(*bmain);
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 23)) {
|
||||
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
|
||||
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
|
||||
if (md->type != eModifierType_Nodes) {
|
||||
continue;
|
||||
}
|
||||
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
|
||||
if (nmd.bake_target == NODES_MODIFIER_BAKE_TARGET_INHERIT) {
|
||||
/* Use disk target for existing modifiers to avoid changing behavior. */
|
||||
nmd.bake_target = NODES_MODIFIER_BAKE_TARGET_DISK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
||||
@@ -25,12 +25,14 @@
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BKE_bake_geometry_nodes_modifier.hh"
|
||||
#include "BKE_bake_geometry_nodes_modifier_pack.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_packedFile.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
@@ -196,12 +198,6 @@ static bool bake_simulation_poll(bContext *C)
|
||||
if (!ED_operator_object_active(C)) {
|
||||
return false;
|
||||
}
|
||||
Main *bmain = CTX_data_main(C);
|
||||
const StringRefNull path = BKE_main_blendfile_path(bmain);
|
||||
if (path.is_empty()) {
|
||||
CTX_wm_operator_poll_msg_set(C, "File must be saved before baking");
|
||||
return false;
|
||||
}
|
||||
Object *ob = context_active_object(C);
|
||||
const bool use_frame_cache = ob->flag & OB_FLAG_USE_SIMULATION_CACHE;
|
||||
if (!use_frame_cache) {
|
||||
@@ -217,7 +213,8 @@ struct NodeBakeRequest {
|
||||
int bake_id;
|
||||
int node_type;
|
||||
|
||||
bake::BakePath path;
|
||||
/** Store bake in this location if available, otherwise pack the baked data. */
|
||||
std::optional<bake::BakePath> path;
|
||||
int frame_start;
|
||||
int frame_end;
|
||||
std::unique_ptr<bake::BlobWriteSharing> blob_sharing;
|
||||
@@ -272,6 +269,19 @@ static void bake_geometry_nodes_startjob(void *customdata, wmJobWorkerStatus *wo
|
||||
const float progress_per_frame = frame_step_size / frames_to_bake;
|
||||
const int old_frame = job.scene->r.cfra;
|
||||
|
||||
struct MemoryBakeFile {
|
||||
std::string name;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
struct PackedBake {
|
||||
Vector<MemoryBakeFile> meta_files;
|
||||
Vector<MemoryBakeFile> blob_files;
|
||||
};
|
||||
|
||||
Map<NodeBakeRequest *, PackedBake> packed_data_by_bake;
|
||||
Map<NodeBakeRequest *, int64_t> size_by_bake;
|
||||
|
||||
for (float frame_f = global_bake_start_frame; frame_f <= global_bake_end_frame;
|
||||
frame_f += frame_step_size)
|
||||
{
|
||||
@@ -307,23 +317,99 @@ static void bake_geometry_nodes_startjob(void *customdata, wmJobWorkerStatus *wo
|
||||
continue;
|
||||
}
|
||||
|
||||
const bake::BakePath path = request.path;
|
||||
int64_t &written_size = size_by_bake.lookup_or_add(&request, 0);
|
||||
|
||||
char meta_path[FILE_MAX];
|
||||
BLI_path_join(meta_path,
|
||||
sizeof(meta_path),
|
||||
path.meta_dir.c_str(),
|
||||
(frame_file_name + ".json").c_str());
|
||||
BLI_file_ensure_parent_dir_exists(meta_path);
|
||||
bake::DiskBlobWriter blob_writer{path.blobs_dir, frame_file_name};
|
||||
fstream meta_file{meta_path, std::ios::out};
|
||||
bake::serialize_bake(frame_cache.state, blob_writer, *request.blob_sharing, meta_file);
|
||||
if (request.path.has_value()) {
|
||||
char meta_path[FILE_MAX];
|
||||
BLI_path_join(meta_path,
|
||||
sizeof(meta_path),
|
||||
request.path->meta_dir.c_str(),
|
||||
(frame_file_name + ".json").c_str());
|
||||
BLI_file_ensure_parent_dir_exists(meta_path);
|
||||
bake::DiskBlobWriter blob_writer{request.path->blobs_dir, frame_file_name};
|
||||
fstream meta_file{meta_path, std::ios::out};
|
||||
bake::serialize_bake(frame_cache.state, blob_writer, *request.blob_sharing, meta_file);
|
||||
written_size += blob_writer.written_size();
|
||||
written_size += meta_file.tellp();
|
||||
}
|
||||
else {
|
||||
PackedBake &packed_data = packed_data_by_bake.lookup_or_add_default(&request);
|
||||
|
||||
bake::MemoryBlobWriter blob_writer{frame_file_name};
|
||||
std::ostringstream meta_file{std::ios::binary};
|
||||
bake::serialize_bake(frame_cache.state, blob_writer, *request.blob_sharing, meta_file);
|
||||
|
||||
packed_data.meta_files.append({frame_file_name + ".json", meta_file.str()});
|
||||
const Map<std::string, bake::MemoryBlobWriter::OutputStream> &blob_stream_by_name =
|
||||
blob_writer.get_stream_by_name();
|
||||
for (auto &&item : blob_stream_by_name.items()) {
|
||||
std::string data = item.value.stream->str();
|
||||
if (data.empty()) {
|
||||
continue;
|
||||
}
|
||||
packed_data.blob_files.append({item.key, std::move(data)});
|
||||
}
|
||||
written_size += blob_writer.written_size();
|
||||
written_size += meta_file.tellp();
|
||||
}
|
||||
}
|
||||
|
||||
worker_status->progress += progress_per_frame;
|
||||
worker_status->do_update = true;
|
||||
}
|
||||
|
||||
/* Update bake sizes. */
|
||||
for (NodeBakeRequest &request : job.bake_requests) {
|
||||
NodesModifierBake *bake = request.nmd->find_bake(request.bake_id);
|
||||
bake->bake_size = size_by_bake.lookup_default(&request, 0);
|
||||
}
|
||||
|
||||
/* Store gathered data as packed data. */
|
||||
for (NodeBakeRequest &request : job.bake_requests) {
|
||||
NodesModifierBake *bake = request.nmd->find_bake(request.bake_id);
|
||||
|
||||
PackedBake *packed_data = packed_data_by_bake.lookup_ptr(&request);
|
||||
if (!packed_data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NodesModifierPackedBake *packed_bake = MEM_cnew<NodesModifierPackedBake>(__func__);
|
||||
|
||||
packed_bake->meta_files_num = packed_data->meta_files.size();
|
||||
packed_bake->blob_files_num = packed_data->blob_files.size();
|
||||
|
||||
packed_bake->meta_files = MEM_cnew_array<NodesModifierBakeFile>(packed_bake->meta_files_num,
|
||||
__func__);
|
||||
packed_bake->blob_files = MEM_cnew_array<NodesModifierBakeFile>(packed_bake->blob_files_num,
|
||||
__func__);
|
||||
|
||||
auto transfer_to_bake =
|
||||
[&](NodesModifierBakeFile *bake_files, MemoryBakeFile *memory_bake_files, const int num) {
|
||||
for (const int i : IndexRange(num)) {
|
||||
NodesModifierBakeFile &bake_file = bake_files[i];
|
||||
MemoryBakeFile &memory = memory_bake_files[i];
|
||||
bake_file.name = BLI_strdup_null(memory.name.c_str());
|
||||
const int64_t data_size = memory.data.size();
|
||||
if (data_size == 0) {
|
||||
continue;
|
||||
}
|
||||
const auto *sharing_info = new blender::ImplicitSharedValue<std::string>(
|
||||
std::move(memory.data));
|
||||
const void *data = sharing_info->data.data();
|
||||
bake_file.packed_file = BKE_packedfile_new_from_memory(data, data_size, sharing_info);
|
||||
}
|
||||
};
|
||||
|
||||
transfer_to_bake(
|
||||
packed_bake->meta_files, packed_data->meta_files.data(), packed_bake->meta_files_num);
|
||||
transfer_to_bake(
|
||||
packed_bake->blob_files, packed_data->blob_files.data(), packed_bake->blob_files_num);
|
||||
|
||||
/* Should have been freed before. */
|
||||
BLI_assert(bake->packed == nullptr);
|
||||
bake->packed = packed_bake;
|
||||
}
|
||||
|
||||
/* Tag simulations as being baked. */
|
||||
for (NodeBakeRequest &request : job.bake_requests) {
|
||||
if (request.node_type != GEO_NODE_SIMULATION_OUTPUT) {
|
||||
@@ -411,6 +497,11 @@ static void try_delete_bake(
|
||||
}
|
||||
clear_data_block_references(*bake);
|
||||
|
||||
if (bake->packed) {
|
||||
nodes_modifier_packed_bake_free(bake->packed);
|
||||
bake->packed = nullptr;
|
||||
}
|
||||
|
||||
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
|
||||
*bmain, object, nmd, bake_id);
|
||||
if (!bake_path) {
|
||||
@@ -534,16 +625,14 @@ static Vector<NodeBakeRequest> collect_simulations_to_bake(Main &bmain,
|
||||
request.bake_id = id;
|
||||
request.node_type = node->type;
|
||||
request.blob_sharing = std::make_unique<bake::BlobWriteSharing>();
|
||||
std::optional<bake::BakePath> path = bake::get_node_bake_path(bmain, *object, *nmd, id);
|
||||
if (!path) {
|
||||
continue;
|
||||
if (bake::get_node_bake_target(*object, *nmd, id) == NODES_MODIFIER_BAKE_TARGET_DISK) {
|
||||
request.path = bake::get_node_bake_path(bmain, *object, *nmd, id);
|
||||
}
|
||||
std::optional<IndexRange> frame_range = bake::get_node_bake_frame_range(
|
||||
scene, *object, *nmd, id);
|
||||
if (!frame_range) {
|
||||
continue;
|
||||
}
|
||||
request.path = std::move(*path);
|
||||
request.frame_start = frame_range->first();
|
||||
request.frame_end = frame_range->last();
|
||||
|
||||
@@ -620,6 +709,44 @@ static bool bake_directory_has_data(const StringRefNull absolute_bake_dir)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool may_have_disk_bake(const NodesModifierData &nmd)
|
||||
{
|
||||
if (nmd.bake_target == NODES_MODIFIER_BAKE_TARGET_DISK) {
|
||||
return true;
|
||||
}
|
||||
for (const NodesModifierBake &bake : Span{nmd.bakes, nmd.bakes_num}) {
|
||||
if (bake.bake_target == NODES_MODIFIER_BAKE_TARGET_DISK) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void initialize_modifier_bake_directory_if_necessary(bContext *C,
|
||||
Object &object,
|
||||
NodesModifierData &nmd,
|
||||
wmOperator *op)
|
||||
{
|
||||
const bool bake_directory_set = !StringRef(nmd.bake_directory).is_empty();
|
||||
if (bake_directory_set) {
|
||||
return;
|
||||
}
|
||||
if (!may_have_disk_bake(nmd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
BKE_reportf(op->reports,
|
||||
RPT_INFO,
|
||||
"Bake directory of object %s, modifier %s is empty, setting default path",
|
||||
object.id.name + 2,
|
||||
nmd.modifier.name);
|
||||
|
||||
nmd.bake_directory = BLI_strdup(
|
||||
bake::get_default_modifier_bake_directory(*bmain, object, nmd).c_str());
|
||||
}
|
||||
|
||||
static void bake_simulation_validate_paths(bContext *C,
|
||||
wmOperator *op,
|
||||
const Span<Object *> objects)
|
||||
@@ -635,18 +762,8 @@ static void bake_simulation_validate_paths(bContext *C,
|
||||
if (md->type != eModifierType_Nodes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
|
||||
if (StringRef(nmd->bake_directory).is_empty()) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_INFO,
|
||||
"Bake directory of object %s, modifier %s is empty, setting default path",
|
||||
object->id.name + 2,
|
||||
md->name);
|
||||
|
||||
nmd->bake_directory = BLI_strdup(
|
||||
bake::get_default_modifier_bake_directory(*bmain, *object, *nmd).c_str());
|
||||
}
|
||||
initialize_modifier_bake_directory_if_necessary(C, *object, *nmd, op);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -702,7 +819,7 @@ static int bake_simulation_invoke(bContext *C, wmOperator *op, const wmEvent * /
|
||||
}
|
||||
}
|
||||
|
||||
/* Set empty paths to default. */
|
||||
/* Set empty paths to default if necessary. */
|
||||
bake_simulation_validate_paths(C, op, objects);
|
||||
|
||||
PathUsersMap path_users = bake_simulation_get_path_users(C, objects);
|
||||
@@ -815,10 +932,7 @@ static Vector<NodeBakeRequest> bake_single_node_gather_bake_request(bContext *C,
|
||||
return {};
|
||||
}
|
||||
|
||||
if (StringRef(nmd.bake_directory).is_empty()) {
|
||||
const std::string directory = bake::get_default_modifier_bake_directory(*bmain, *object, nmd);
|
||||
nmd.bake_directory = BLI_strdup(directory.c_str());
|
||||
}
|
||||
initialize_modifier_bake_directory_if_necessary(C, *object, nmd, op);
|
||||
|
||||
const int bake_id = RNA_int_get(op->ptr, "bake_id");
|
||||
const bNode *node = nmd.node_group->find_nested_node(bake_id);
|
||||
@@ -840,13 +954,14 @@ static Vector<NodeBakeRequest> bake_single_node_gather_bake_request(bContext *C,
|
||||
if (!bake) {
|
||||
return {};
|
||||
}
|
||||
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
|
||||
*bmain, *object, nmd, bake_id);
|
||||
if (!bake_path.has_value()) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Cannot determine bake location on disk");
|
||||
return {};
|
||||
if (bake::get_node_bake_target(*object, nmd, bake_id) == NODES_MODIFIER_BAKE_TARGET_DISK) {
|
||||
request.path = bake::get_node_bake_path(*bmain, *object, nmd, bake_id);
|
||||
if (!request.path) {
|
||||
BKE_report(op->reports,
|
||||
RPT_INFO,
|
||||
"Can't determine bake location on disk. Falling back to packed bake.");
|
||||
}
|
||||
}
|
||||
request.path = std::move(*bake_path);
|
||||
|
||||
if (node->type == GEO_NODE_BAKE && bake->bake_mode == NODES_MODIFIER_BAKE_MODE_STILL) {
|
||||
const int current_frame = scene->r.cfra;
|
||||
@@ -929,25 +1044,101 @@ static int delete_single_bake_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool bake_poll(bContext *C)
|
||||
static int pack_single_bake_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
if (BKE_main_blendfile_path(bmain)[0] == '\0') {
|
||||
/* Saving the .blend file is not technically necessary in all cases but only when the bake path
|
||||
* depends on the .blend file path (which is the case by default). */
|
||||
CTX_wm_operator_poll_msg_set(C, "File must be saved before baking");
|
||||
return false;
|
||||
Object *object = reinterpret_cast<Object *>(
|
||||
WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_OB));
|
||||
if (object == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
return true;
|
||||
char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
|
||||
if (modifier_name == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(modifier_name); });
|
||||
|
||||
ModifierData *md = BKE_modifiers_findby_name(object, modifier_name);
|
||||
if (md == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
|
||||
const int bake_id = RNA_int_get(op->ptr, "bake_id");
|
||||
|
||||
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
|
||||
*bmain, *object, nmd, bake_id);
|
||||
if (!bake_path) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
NodesModifierBake *bake = nmd.find_bake(bake_id);
|
||||
if (!bake) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bake::pack_geometry_nodes_bake(*bmain, op->reports, *object, nmd, *bake);
|
||||
|
||||
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr);
|
||||
WM_main_add_notifier(NC_NODE, nullptr);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool bake_delete_poll(bContext *C)
|
||||
static int unpack_single_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
uiPopupMenu *pup;
|
||||
uiLayout *layout;
|
||||
|
||||
pup = UI_popup_menu_begin(C, IFACE_("Unpack"), ICON_NONE);
|
||||
layout = UI_popup_menu_layout(pup);
|
||||
|
||||
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
|
||||
uiItemsFullEnumO(layout,
|
||||
op->type->idname,
|
||||
"method",
|
||||
static_cast<IDProperty *>(op->ptr->data),
|
||||
WM_OP_EXEC_REGION_WIN,
|
||||
UI_ITEM_NONE);
|
||||
|
||||
UI_popup_menu_end(C, pup);
|
||||
|
||||
return OPERATOR_INTERFACE;
|
||||
}
|
||||
|
||||
static int unpack_single_bake_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
if (BKE_main_blendfile_path(bmain)[0] == '\0') {
|
||||
return false;
|
||||
Object *object = reinterpret_cast<Object *>(
|
||||
WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_OB));
|
||||
if (object == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
return true;
|
||||
char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
|
||||
if (modifier_name == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(modifier_name); });
|
||||
|
||||
ModifierData *md = BKE_modifiers_findby_name(object, modifier_name);
|
||||
if (md == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
|
||||
const int bake_id = RNA_int_get(op->ptr, "bake_id");
|
||||
NodesModifierBake *bake = nmd.find_bake(bake_id);
|
||||
if (!bake) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const ePF_FileStatus method = ePF_FileStatus(RNA_enum_get(op->ptr, "method"));
|
||||
|
||||
bake::UnpackGeometryNodesBakeResult result = bake::unpack_geometry_nodes_bake(
|
||||
*bmain, op->reports, *object, nmd, *bake, method);
|
||||
if (result != bake::UnpackGeometryNodesBakeResult::Success) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr);
|
||||
WM_main_add_notifier(NC_NODE, nullptr);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot)
|
||||
@@ -1014,7 +1205,6 @@ void OBJECT_OT_geometry_node_bake_single(wmOperatorType *ot)
|
||||
ot->description = "Bake a single bake node or simulation";
|
||||
ot->idname = "OBJECT_OT_geometry_node_bake_single";
|
||||
|
||||
ot->poll = bake_poll;
|
||||
ot->invoke = bake_single_node_invoke;
|
||||
ot->exec = bake_single_node_exec;
|
||||
ot->modal = bake_single_node_modal;
|
||||
@@ -1028,10 +1218,58 @@ void OBJECT_OT_geometry_node_bake_delete_single(wmOperatorType *ot)
|
||||
ot->description = "Delete baked data of a single bake node or simulation";
|
||||
ot->idname = "OBJECT_OT_geometry_node_bake_delete_single";
|
||||
|
||||
ot->poll = bake_delete_poll;
|
||||
ot->exec = delete_single_bake_exec;
|
||||
|
||||
single_bake_operator_props(ot);
|
||||
}
|
||||
|
||||
void OBJECT_OT_geometry_node_bake_pack_single(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Pack Geometry Node Bake";
|
||||
ot->description = "Pack baked data from disk into the .blend file";
|
||||
ot->idname = "OBJECT_OT_geometry_node_bake_pack_single";
|
||||
|
||||
ot->exec = pack_single_bake_exec;
|
||||
|
||||
single_bake_operator_props(ot);
|
||||
}
|
||||
|
||||
void OBJECT_OT_geometry_node_bake_unpack_single(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Unpack Geometry Node Bake";
|
||||
ot->description = "Unpack baked data from the .blend file to disk";
|
||||
ot->idname = "OBJECT_OT_geometry_node_bake_unpack_single";
|
||||
|
||||
ot->exec = unpack_single_bake_exec;
|
||||
ot->invoke = unpack_single_bake_invoke;
|
||||
|
||||
single_bake_operator_props(ot);
|
||||
|
||||
static const EnumPropertyItem method_items[] = {
|
||||
{PF_USE_LOCAL,
|
||||
"USE_LOCAL",
|
||||
0,
|
||||
"Use bake from current directory (create when necessary)",
|
||||
""},
|
||||
{PF_WRITE_LOCAL,
|
||||
"WRITE_LOCAL",
|
||||
0,
|
||||
"Write bake to current directory (overwrite existing bake)",
|
||||
""},
|
||||
{PF_USE_ORIGINAL,
|
||||
"USE_ORIGINAL",
|
||||
0,
|
||||
"Use bake in original location (create when necessary)",
|
||||
""},
|
||||
{PF_WRITE_ORIGINAL,
|
||||
"WRITE_ORIGINAL",
|
||||
0,
|
||||
"Write bake to original location (overwrite existing file)",
|
||||
""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
RNA_def_enum(ot->srna, "method", method_items, PF_USE_LOCAL, "Method", "How to unpack");
|
||||
}
|
||||
|
||||
} // namespace blender::ed::object::bake_simulation
|
||||
|
||||
@@ -342,6 +342,8 @@ void OBJECT_OT_simulation_nodes_cache_bake(wmOperatorType *ot);
|
||||
void OBJECT_OT_simulation_nodes_cache_delete(wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_node_bake_single(wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_node_bake_delete_single(wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_node_bake_pack_single(wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_node_bake_unpack_single(wmOperatorType *ot);
|
||||
|
||||
} // namespace bake_simulation
|
||||
|
||||
|
||||
@@ -250,6 +250,8 @@ void operatortypes_object()
|
||||
WM_operatortype_append(bake_simulation::OBJECT_OT_simulation_nodes_cache_delete);
|
||||
WM_operatortype_append(bake_simulation::OBJECT_OT_geometry_node_bake_single);
|
||||
WM_operatortype_append(bake_simulation::OBJECT_OT_geometry_node_bake_delete_single);
|
||||
WM_operatortype_append(bake_simulation::OBJECT_OT_geometry_node_bake_pack_single);
|
||||
WM_operatortype_append(bake_simulation::OBJECT_OT_geometry_node_bake_unpack_single);
|
||||
WM_operatortype_append(OBJECT_OT_drop_named_material);
|
||||
WM_operatortype_append(OBJECT_OT_drop_geometry_nodes);
|
||||
WM_operatortype_append(OBJECT_OT_unlink_data);
|
||||
|
||||
@@ -38,6 +38,7 @@ set(LIB
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::extern::fmtlib
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
@@ -158,6 +159,8 @@ static int pack_all_exec(bContext *C, wmOperator *op)
|
||||
|
||||
BKE_packedfile_pack_all(bmain, op->reports, true);
|
||||
|
||||
WM_main_add_notifier(NC_WINDOW, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
@@ -244,6 +247,7 @@ static int unpack_all_exec(bContext *C, wmOperator *op)
|
||||
WM_cursor_wait(false);
|
||||
}
|
||||
G.fileflags &= ~G_FILE_AUTOPACK;
|
||||
WM_main_add_notifier(NC_WINDOW, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
@@ -253,25 +257,19 @@ static int unpack_all_invoke(bContext *C, wmOperator *op, const wmEvent * /*even
|
||||
Main *bmain = CTX_data_main(C);
|
||||
uiPopupMenu *pup;
|
||||
uiLayout *layout;
|
||||
char title[64];
|
||||
int count = 0;
|
||||
|
||||
count = BKE_packedfile_count_all(bmain);
|
||||
const PackedFileCount count = BKE_packedfile_count_all(bmain);
|
||||
|
||||
if (!count) {
|
||||
if (count.total() == 0) {
|
||||
BKE_report(op->reports, RPT_WARNING, "No packed files to unpack");
|
||||
G.fileflags &= ~G_FILE_AUTOPACK;
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (count == 1) {
|
||||
STRNCPY_UTF8(title, IFACE_("Unpack 1 File"));
|
||||
}
|
||||
else {
|
||||
SNPRINTF(title, IFACE_("Unpack %d Files"), count);
|
||||
}
|
||||
const std::string title = fmt::format(
|
||||
IFACE_("Unpack - Files: {}, Bakes: {}"), count.individual_files, count.bakes);
|
||||
|
||||
pup = UI_popup_menu_begin(C, title, ICON_NONE);
|
||||
pup = UI_popup_menu_begin(C, title.c_str(), ICON_NONE);
|
||||
layout = UI_popup_menu_layout(pup);
|
||||
|
||||
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
|
||||
|
||||
@@ -563,7 +563,9 @@
|
||||
}
|
||||
|
||||
#define _DNA_DEFAULT_NodesModifierData \
|
||||
{ 0 }
|
||||
{ \
|
||||
.bake_target = NODES_MODIFIER_BAKE_TARGET_PACKED, \
|
||||
}
|
||||
|
||||
#define _DNA_DEFAULT_SkinModifierData \
|
||||
{ \
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "DNA_defs.h"
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_packedFile_types.h"
|
||||
#include "DNA_session_uid_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -2403,6 +2404,33 @@ typedef struct NodesModifierDataBlock {
|
||||
char _pad[4];
|
||||
} NodesModifierDataBlock;
|
||||
|
||||
typedef struct NodesModifierBakeFile {
|
||||
const char *name;
|
||||
/* May be null if the file is empty. */
|
||||
PackedFile *packed_file;
|
||||
|
||||
#ifdef __cplusplus
|
||||
blender::Span<std::byte> data() const
|
||||
{
|
||||
if (this->packed_file) {
|
||||
return blender::Span{static_cast<const std::byte *>(this->packed_file->data),
|
||||
this->packed_file->size};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
} NodesModifierBakeFile;
|
||||
|
||||
/**
|
||||
* A packed bake. The format is the same as if the bake was stored on disk.
|
||||
*/
|
||||
typedef struct NodesModifierPackedBake {
|
||||
int meta_files_num;
|
||||
int blob_files_num;
|
||||
NodesModifierBakeFile *meta_files;
|
||||
NodesModifierBakeFile *blob_files;
|
||||
} NodesModifierPackedBake;
|
||||
|
||||
typedef struct NodesModifierBake {
|
||||
/** An id that references a nested node in the node tree. Also see #bNestedNodeRef. */
|
||||
int id;
|
||||
@@ -2410,7 +2438,9 @@ typedef struct NodesModifierBake {
|
||||
uint32_t flag;
|
||||
/** #NodesModifierBakeMode. */
|
||||
uint8_t bake_mode;
|
||||
char _pad[7];
|
||||
/** #NodesModifierBakeTarget. */
|
||||
int8_t bake_target;
|
||||
char _pad[6];
|
||||
/**
|
||||
* Directory where the baked data should be stored. This is only used when
|
||||
* `NODES_MODIFIER_BAKE_CUSTOM_PATH` is set.
|
||||
@@ -2433,6 +2463,10 @@ typedef struct NodesModifierBake {
|
||||
int data_blocks_num;
|
||||
int active_data_block;
|
||||
NodesModifierDataBlock *data_blocks;
|
||||
NodesModifierPackedBake *packed;
|
||||
|
||||
void *_pad2;
|
||||
int64_t bake_size;
|
||||
} NodesModifierBake;
|
||||
|
||||
typedef struct NodesModifierPanel {
|
||||
@@ -2451,6 +2485,12 @@ typedef enum NodesModifierBakeFlag {
|
||||
NODES_MODIFIER_BAKE_CUSTOM_PATH = 1 << 1,
|
||||
} NodesModifierBakeFlag;
|
||||
|
||||
typedef enum NodesModifierBakeTarget {
|
||||
NODES_MODIFIER_BAKE_TARGET_INHERIT = 0,
|
||||
NODES_MODIFIER_BAKE_TARGET_PACKED = 1,
|
||||
NODES_MODIFIER_BAKE_TARGET_DISK = 2,
|
||||
} NodesModifierBakeTarget;
|
||||
|
||||
typedef enum NodesModifierBakeMode {
|
||||
NODES_MODIFIER_BAKE_MODE_ANIMATION = 0,
|
||||
NODES_MODIFIER_BAKE_MODE_STILL = 1,
|
||||
@@ -2466,8 +2506,10 @@ typedef struct NodesModifierData {
|
||||
char *bake_directory;
|
||||
/** NodesModifierFlag. */
|
||||
int8_t flag;
|
||||
/** #NodesModifierBakeTarget. */
|
||||
int8_t bake_target;
|
||||
|
||||
char _pad[3];
|
||||
char _pad[2];
|
||||
int bakes_num;
|
||||
NodesModifierBake *bakes;
|
||||
|
||||
|
||||
@@ -941,6 +941,12 @@ static void rna_Modifier_dependency_update(Main *bmain, Scene *scene, PointerRNA
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
|
||||
static void rna_NodesModifier_bake_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
rna_Modifier_update(bmain, scene, ptr);
|
||||
WM_main_add_notifier(NC_NODE | NA_EDITED, ptr->owner_id);
|
||||
}
|
||||
|
||||
static void rna_Modifier_is_active_set(PointerRNA *ptr, bool value)
|
||||
{
|
||||
ModifierData *md = static_cast<ModifierData *>(ptr->data);
|
||||
@@ -7868,6 +7874,39 @@ static void rna_def_modifier_nodes_bake_data_blocks(BlenderRNA *brna)
|
||||
RNA_def_property_int_sdna(prop, nullptr, "active_data_block");
|
||||
}
|
||||
|
||||
static EnumPropertyItem bake_target_in_node_items[] = {
|
||||
{NODES_MODIFIER_BAKE_TARGET_INHERIT,
|
||||
"INHERIT",
|
||||
0,
|
||||
"Inherit from Modifier",
|
||||
"Use setting from the modifier"},
|
||||
{NODES_MODIFIER_BAKE_TARGET_PACKED,
|
||||
"PACKED",
|
||||
0,
|
||||
"Packed",
|
||||
"Pack the baked data into the .blend file"},
|
||||
{NODES_MODIFIER_BAKE_TARGET_DISK,
|
||||
"DISK",
|
||||
0,
|
||||
"Disk",
|
||||
"Store the baked data in a directory on disk"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static EnumPropertyItem bake_target_in_modifier_items[] = {
|
||||
{NODES_MODIFIER_BAKE_TARGET_PACKED,
|
||||
"PACKED",
|
||||
0,
|
||||
"Packed",
|
||||
"Pack the baked data into the .blend file"},
|
||||
{NODES_MODIFIER_BAKE_TARGET_DISK,
|
||||
"DISK",
|
||||
0,
|
||||
"Disk",
|
||||
"Store the baked data in a directory on disk"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
|
||||
{
|
||||
rna_def_modifier_nodes_bake_data_blocks(brna);
|
||||
@@ -7888,33 +7927,38 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
|
||||
|
||||
prop = RNA_def_property(srna, "directory", PROP_STRING, PROP_DIRPATH);
|
||||
RNA_def_property_ui_text(prop, "Directory", "Location on disk where the bake data is stored");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
RNA_def_property_update(prop, 0, "rna_NodesModifier_bake_update");
|
||||
|
||||
prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_TIME);
|
||||
RNA_def_property_ui_text(prop, "Start Frame", "Frame where the baking starts");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
RNA_def_property_update(prop, 0, "rna_NodesModifier_bake_update");
|
||||
|
||||
prop = RNA_def_property(srna, "frame_end", PROP_INT, PROP_TIME);
|
||||
RNA_def_property_ui_text(prop, "End Frame", "Frame where the baking ends");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
RNA_def_property_update(prop, 0, "rna_NodesModifier_bake_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_custom_simulation_frame_range", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(
|
||||
prop, nullptr, "flag", NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Custom Simulation Frame Range", "Override the simulation frame range from the scene");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
RNA_def_property_update(prop, 0, "rna_NodesModifier_bake_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_custom_path", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODES_MODIFIER_BAKE_CUSTOM_PATH);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Custom Path", "Specify a path where the baked data should be stored manually");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
RNA_def_property_update(prop, 0, "rna_NodesModifier_bake_update");
|
||||
|
||||
prop = RNA_def_property(srna, "bake_target", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, bake_target_in_node_items);
|
||||
RNA_def_property_ui_text(prop, "Bake Target", "Where to store the baked data");
|
||||
RNA_def_property_update(prop, 0, "rna_NodesModifier_bake_update");
|
||||
|
||||
prop = RNA_def_property(srna, "bake_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, bake_mode_items);
|
||||
RNA_def_property_ui_text(prop, "Bake Mode", "");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
RNA_def_property_update(prop, 0, "rna_NodesModifier_bake_update");
|
||||
|
||||
prop = RNA_def_property(srna, "bake_id", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
@@ -8034,7 +8078,12 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
|
||||
prop = RNA_def_property(srna, "bake_directory", PROP_STRING, PROP_DIRPATH);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Simulation Bake Directory", "Location on disk where the bake data is stored");
|
||||
RNA_def_property_update(prop, 0, nullptr);
|
||||
RNA_def_property_update(prop, 0, "rna_NodesModifier_bake_update");
|
||||
|
||||
prop = RNA_def_property(srna, "bake_target", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, bake_target_in_modifier_items);
|
||||
RNA_def_property_ui_text(prop, "Bake Target", "Where to store the baked data");
|
||||
RNA_def_property_update(prop, 0, "rna_NodesModifier_bake_update");
|
||||
|
||||
prop = RNA_def_property(srna, "bakes", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "NodesModifierBake");
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
struct NodesModifierData;
|
||||
struct Object;
|
||||
struct NodesModifierPackedBake;
|
||||
|
||||
namespace blender::bke::bake {
|
||||
struct ModifierCache;
|
||||
@@ -40,5 +41,6 @@ struct NodesModifierRuntime {
|
||||
};
|
||||
|
||||
void nodes_modifier_data_block_destruct(NodesModifierDataBlock *data_block, bool do_id_user);
|
||||
void nodes_modifier_packed_bake_free(NodesModifierPackedBake *packed_bake);
|
||||
|
||||
} // namespace blender
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_update.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_packedFile.hh"
|
||||
#include "BKE_pointcloud.hh"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_workspace.hh"
|
||||
@@ -968,15 +969,33 @@ static void ensure_bake_loaded(bake::NodeBakeCache &bake_cache, bake::FrameCache
|
||||
if (!frame_cache.state.items_by_id.is_empty()) {
|
||||
return;
|
||||
}
|
||||
if (!frame_cache.meta_data_source.has_value()) {
|
||||
return;
|
||||
}
|
||||
if (bake_cache.memory_blob_reader) {
|
||||
if (const auto *meta_buffer = std::get_if<Span<std::byte>>(&*frame_cache.meta_data_source)) {
|
||||
const std::string meta_str{reinterpret_cast<const char *>(meta_buffer->data()),
|
||||
size_t(meta_buffer->size())};
|
||||
std::istringstream meta_stream{meta_str};
|
||||
std::optional<bake::BakeState> bake_state = bake::deserialize_bake(
|
||||
meta_stream, *bake_cache.memory_blob_reader, *bake_cache.blob_sharing);
|
||||
if (!bake_state.has_value()) {
|
||||
return;
|
||||
}
|
||||
frame_cache.state = std::move(*bake_state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!bake_cache.blobs_dir) {
|
||||
return;
|
||||
}
|
||||
if (!frame_cache.meta_path) {
|
||||
const auto *meta_path = std::get_if<std::string>(&*frame_cache.meta_data_source);
|
||||
if (!meta_path) {
|
||||
return;
|
||||
}
|
||||
bke::bake::DiskBlobReader blob_reader{*bake_cache.blobs_dir};
|
||||
fstream meta_file{*frame_cache.meta_path};
|
||||
std::optional<bke::bake::BakeState> bake_state = bke::bake::deserialize_bake(
|
||||
bake::DiskBlobReader blob_reader{*bake_cache.blobs_dir};
|
||||
fstream meta_file{*meta_path};
|
||||
std::optional<bake::BakeState> bake_state = bake::deserialize_bake(
|
||||
meta_file, blob_reader, *bake_cache.blob_sharing);
|
||||
if (!bake_state.has_value()) {
|
||||
return;
|
||||
@@ -984,12 +1003,53 @@ static void ensure_bake_loaded(bake::NodeBakeCache &bake_cache, bake::FrameCache
|
||||
frame_cache.state = std::move(*bake_state);
|
||||
}
|
||||
|
||||
static bool try_find_baked_data(bake::NodeBakeCache &bake,
|
||||
static bool try_find_baked_data(const NodesModifierBake &bake,
|
||||
bake::NodeBakeCache &bake_cache,
|
||||
const Main &bmain,
|
||||
const Object &object,
|
||||
const NodesModifierData &nmd,
|
||||
const int id)
|
||||
{
|
||||
if (bake.packed) {
|
||||
if (bake.packed->meta_files_num == 0) {
|
||||
return false;
|
||||
}
|
||||
bake_cache.reset();
|
||||
Map<SubFrame, const NodesModifierBakeFile *> file_by_frame;
|
||||
for (const NodesModifierBakeFile &meta_file :
|
||||
Span{bake.packed->meta_files, bake.packed->meta_files_num})
|
||||
{
|
||||
const std::optional<SubFrame> frame = bake::file_name_to_frame(meta_file.name);
|
||||
if (!frame) {
|
||||
return false;
|
||||
}
|
||||
if (!file_by_frame.add(*frame, &meta_file)) {
|
||||
/* Can only have on file per (sub)frame. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* Make sure frames processed in the right order. */
|
||||
Vector<SubFrame> frames;
|
||||
frames.extend(file_by_frame.keys().begin(), file_by_frame.keys().end());
|
||||
|
||||
for (const SubFrame &frame : frames) {
|
||||
const NodesModifierBakeFile &meta_file = *file_by_frame.lookup(frame);
|
||||
auto frame_cache = std::make_unique<bake::FrameCache>();
|
||||
frame_cache->frame = frame;
|
||||
frame_cache->meta_data_source = meta_file.data();
|
||||
bake_cache.frames.append(std::move(frame_cache));
|
||||
}
|
||||
|
||||
bake_cache.memory_blob_reader = std::make_unique<bake::MemoryBlobReader>();
|
||||
for (const NodesModifierBakeFile &blob_file :
|
||||
Span{bake.packed->blob_files, bake.packed->blob_files_num})
|
||||
{
|
||||
bake_cache.memory_blob_reader->add(blob_file.name, blob_file.data());
|
||||
}
|
||||
bake_cache.blob_sharing = std::make_unique<bake::BlobReadSharing>();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(bmain, object, nmd, id);
|
||||
if (!bake_path) {
|
||||
return false;
|
||||
@@ -998,15 +1058,15 @@ static bool try_find_baked_data(bake::NodeBakeCache &bake,
|
||||
if (meta_files.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
bake.reset();
|
||||
bake_cache.reset();
|
||||
for (const bake::MetaFile &meta_file : meta_files) {
|
||||
auto frame_cache = std::make_unique<bake::FrameCache>();
|
||||
frame_cache->frame = meta_file.frame;
|
||||
frame_cache->meta_path = meta_file.path;
|
||||
bake.frames.append(std::move(frame_cache));
|
||||
frame_cache->meta_data_source = meta_file.path;
|
||||
bake_cache.frames.append(std::move(frame_cache));
|
||||
}
|
||||
bake.blobs_dir = bake_path->blobs_dir;
|
||||
bake.blob_sharing = std::make_unique<bake::BlobReadSharing>();
|
||||
bake_cache.blobs_dir = bake_path->blobs_dir;
|
||||
bake_cache.blob_sharing = std::make_unique<bake::BlobReadSharing>();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1141,7 +1201,7 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
/* Try load baked data. */
|
||||
if (!node_cache.bake.failed_finding_bake) {
|
||||
if (node_cache.cache_status != bake::CacheStatus::Baked) {
|
||||
if (try_find_baked_data(node_cache.bake, *bmain_, *ctx_.object, nmd_, zone_id)) {
|
||||
if (try_find_baked_data(bake, node_cache.bake, *bmain_, *ctx_.object, nmd_, zone_id)) {
|
||||
node_cache.cache_status = bake::CacheStatus::Baked;
|
||||
}
|
||||
else {
|
||||
@@ -1434,7 +1494,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
/* Try load baked data. */
|
||||
if (node_cache.bake.frames.is_empty()) {
|
||||
if (!node_cache.bake.failed_finding_bake) {
|
||||
if (!try_find_baked_data(node_cache.bake, *bmain_, *ctx_.object, nmd_, id)) {
|
||||
if (!try_find_baked_data(bake, node_cache.bake, *bmain_, *ctx_.object, nmd_, id)) {
|
||||
node_cache.bake.failed_finding_bake = true;
|
||||
}
|
||||
}
|
||||
@@ -1503,7 +1563,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
[[nodiscard]] bool check_read_error(const bake::FrameCache &frame_cache,
|
||||
nodes::BakeNodeBehavior &behavior) const
|
||||
{
|
||||
if (frame_cache.meta_path && frame_cache.state.items_by_id.is_empty()) {
|
||||
if (frame_cache.meta_data_source && frame_cache.state.items_by_id.is_empty()) {
|
||||
auto &read_error_info = behavior.behavior.emplace<sim_output::ReadError>();
|
||||
read_error_info.message = RPT_("Cannot load the baked data");
|
||||
return true;
|
||||
@@ -2229,6 +2289,7 @@ static void draw_bake_panel(uiLayout *layout, PointerRNA *modifier_ptr)
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetPropSep(col, true);
|
||||
uiLayoutSetPropDecorate(col, false);
|
||||
uiItemR(col, modifier_ptr, "bake_target", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(col, modifier_ptr, "bake_directory", UI_ITEM_NONE, IFACE_("Bake Path"), ICON_NONE);
|
||||
}
|
||||
|
||||
@@ -2411,6 +2472,29 @@ static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const Modi
|
||||
BLO_write_string(writer, item.id_name);
|
||||
BLO_write_string(writer, item.lib_name);
|
||||
}
|
||||
if (bake.packed) {
|
||||
BLO_write_struct(writer, NodesModifierPackedBake, bake.packed);
|
||||
BLO_write_struct_array(
|
||||
writer, NodesModifierBakeFile, bake.packed->meta_files_num, bake.packed->meta_files);
|
||||
BLO_write_struct_array(
|
||||
writer, NodesModifierBakeFile, bake.packed->blob_files_num, bake.packed->blob_files);
|
||||
const auto write_bake_file = [&](const NodesModifierBakeFile &bake_file) {
|
||||
BLO_write_string(writer, bake_file.name);
|
||||
if (bake_file.packed_file) {
|
||||
BKE_packedfile_blend_write(writer, bake_file.packed_file);
|
||||
}
|
||||
};
|
||||
for (const NodesModifierBakeFile &meta_file :
|
||||
Span{bake.packed->meta_files, bake.packed->meta_files_num})
|
||||
{
|
||||
write_bake_file(meta_file);
|
||||
}
|
||||
for (const NodesModifierBakeFile &blob_file :
|
||||
Span{bake.packed->blob_files, bake.packed->blob_files_num})
|
||||
{
|
||||
write_bake_file(blob_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
BLO_write_struct_array(writer, NodesModifierPanel, nmd->panels_num, nmd->panels);
|
||||
|
||||
@@ -2461,6 +2545,30 @@ static void blend_read(BlendDataReader *reader, ModifierData *md)
|
||||
BLO_read_string(reader, &data_block.id_name);
|
||||
BLO_read_string(reader, &data_block.lib_name);
|
||||
}
|
||||
|
||||
BLO_read_struct(reader, NodesModifierPackedBake, &bake.packed);
|
||||
if (bake.packed) {
|
||||
BLO_read_struct_array(
|
||||
reader, NodesModifierBakeFile, bake.packed->meta_files_num, &bake.packed->meta_files);
|
||||
BLO_read_struct_array(
|
||||
reader, NodesModifierBakeFile, bake.packed->blob_files_num, &bake.packed->blob_files);
|
||||
const auto read_bake_file = [&](NodesModifierBakeFile &bake_file) {
|
||||
BLO_read_string(reader, &bake_file.name);
|
||||
if (bake_file.packed_file) {
|
||||
BKE_packedfile_blend_read(reader, &bake_file.packed_file, "");
|
||||
}
|
||||
};
|
||||
for (NodesModifierBakeFile &meta_file :
|
||||
MutableSpan{bake.packed->meta_files, bake.packed->meta_files_num})
|
||||
{
|
||||
read_bake_file(meta_file);
|
||||
}
|
||||
for (NodesModifierBakeFile &blob_file :
|
||||
MutableSpan{bake.packed->blob_files, bake.packed->blob_files_num})
|
||||
{
|
||||
read_bake_file(blob_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
BLO_read_struct_array(reader, NodesModifierPanel, nmd->panels_num, &nmd->panels);
|
||||
|
||||
@@ -2494,6 +2602,24 @@ static void copy_data(const ModifierData *md, ModifierData *target, const int fl
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bake.packed) {
|
||||
bake.packed = static_cast<NodesModifierPackedBake *>(MEM_dupallocN(bake.packed));
|
||||
const auto copy_bake_files_inplace = [](NodesModifierBakeFile **bake_files,
|
||||
const int bake_files_num) {
|
||||
if (!*bake_files) {
|
||||
return;
|
||||
}
|
||||
*bake_files = static_cast<NodesModifierBakeFile *>(MEM_dupallocN(*bake_files));
|
||||
for (NodesModifierBakeFile &bake_file : MutableSpan{*bake_files, bake_files_num}) {
|
||||
bake_file.name = BLI_strdup_null(bake_file.name);
|
||||
if (bake_file.packed_file) {
|
||||
bake_file.packed_file = BKE_packedfile_duplicate(bake_file.packed_file);
|
||||
}
|
||||
}
|
||||
};
|
||||
copy_bake_files_inplace(&bake.packed->meta_files, bake.packed->meta_files_num);
|
||||
copy_bake_files_inplace(&bake.packed->blob_files, bake.packed->blob_files_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2520,6 +2646,22 @@ static void copy_data(const ModifierData *md, ModifierData *target, const int fl
|
||||
}
|
||||
}
|
||||
|
||||
void nodes_modifier_packed_bake_free(NodesModifierPackedBake *packed_bake)
|
||||
{
|
||||
const auto free_packed_files = [](NodesModifierBakeFile *files, const int files_num) {
|
||||
for (NodesModifierBakeFile &file : MutableSpan{files, files_num}) {
|
||||
MEM_SAFE_FREE(file.name);
|
||||
if (file.packed_file) {
|
||||
BKE_packedfile_free(file.packed_file);
|
||||
}
|
||||
}
|
||||
MEM_SAFE_FREE(files);
|
||||
};
|
||||
free_packed_files(packed_bake->meta_files, packed_bake->meta_files_num);
|
||||
free_packed_files(packed_bake->blob_files, packed_bake->blob_files_num);
|
||||
MEM_SAFE_FREE(packed_bake);
|
||||
}
|
||||
|
||||
static void free_data(ModifierData *md)
|
||||
{
|
||||
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
|
||||
@@ -2537,6 +2679,10 @@ static void free_data(ModifierData *md)
|
||||
MEM_SAFE_FREE(data_block.lib_name);
|
||||
}
|
||||
MEM_SAFE_FREE(bake.data_blocks);
|
||||
|
||||
if (bake.packed) {
|
||||
nodes_modifier_packed_bake_free(bake.packed);
|
||||
}
|
||||
}
|
||||
MEM_SAFE_FREE(nmd->bakes);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "NOD_geo_simulation.hh"
|
||||
@@ -90,6 +91,7 @@ struct BakeDrawContext {
|
||||
std::optional<IndexRange> frame_range;
|
||||
bool bake_still;
|
||||
bool is_baked;
|
||||
std::optional<NodesModifierBakeTarget> bake_target;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool get_bake_draw_context(const bContext *C,
|
||||
@@ -97,8 +99,11 @@ struct BakeDrawContext {
|
||||
BakeDrawContext &r_ctx);
|
||||
|
||||
std::string get_baked_string(const BakeDrawContext &ctx);
|
||||
|
||||
std::optional<std::string> get_bake_state_string(const BakeDrawContext &ctx);
|
||||
void draw_common_bake_settings(BakeDrawContext &ctx, uiLayout *layout);
|
||||
void draw_bake_button(const BakeDrawContext &ctx, uiLayout *layout);
|
||||
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);
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_anonymous_attribute_make.hh"
|
||||
#include "BKE_bake_geometry_nodes_modifier.hh"
|
||||
#include "BKE_bake_items_socket.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "ED_node.hh"
|
||||
@@ -578,7 +581,7 @@ static void node_layout(uiLayout *layout, bContext *C, PointerRNA *ptr)
|
||||
uiLayoutSetEnabled(row, !ctx.is_baked);
|
||||
uiItemR(row, &ctx.bake_rna, "bake_mode", UI_ITEM_R_EXPAND, IFACE_("Mode"), ICON_NONE);
|
||||
}
|
||||
draw_bake_button(ctx, col);
|
||||
draw_bake_button_row(ctx, col);
|
||||
}
|
||||
|
||||
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
|
||||
@@ -601,13 +604,14 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
|
||||
uiItemR(row, &ctx.bake_rna, "bake_mode", UI_ITEM_R_EXPAND, IFACE_("Mode"), ICON_NONE);
|
||||
}
|
||||
|
||||
draw_bake_button(ctx, col);
|
||||
draw_bake_button_row(ctx, col, true);
|
||||
if (const std::optional<std::string> bake_state_str = get_bake_state_string(ctx)) {
|
||||
uiItemL(col, bake_state_str->c_str(), ICON_NONE);
|
||||
uiLayout *row = uiLayoutRow(col, true);
|
||||
uiItemL(row, bake_state_str->c_str(), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
draw_common_bake_settings(ctx, layout);
|
||||
draw_common_bake_settings(C, ctx, layout);
|
||||
draw_data_blocks(C, layout, ctx.bake_rna);
|
||||
}
|
||||
|
||||
@@ -721,6 +725,7 @@ bool get_bake_draw_context(const bContext *C, const bNode &node, BakeDrawContext
|
||||
r_ctx.bake_still = node.type == GEO_NODE_BAKE &&
|
||||
r_ctx.bake->bake_mode == NODES_MODIFIER_BAKE_MODE_STILL;
|
||||
r_ctx.is_baked = r_ctx.baked_range.has_value();
|
||||
r_ctx.bake_target = bke::bake::get_node_bake_target(*r_ctx.object, *r_ctx.nmd, r_ctx.bake->id);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -735,11 +740,18 @@ std::string get_baked_string(const BakeDrawContext &ctx)
|
||||
|
||||
std::optional<std::string> get_bake_state_string(const BakeDrawContext &ctx)
|
||||
{
|
||||
if (G.is_rendering) {
|
||||
/* Avoid accessing data that is generated while baking. */
|
||||
return std::nullopt;
|
||||
}
|
||||
if (ctx.is_baked) {
|
||||
if (ctx.bake_still) {
|
||||
return fmt::format(RPT_("Baked Frame {}"), ctx.baked_range->first());
|
||||
const std::string baked_str = get_baked_string(ctx);
|
||||
char size_str[BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE];
|
||||
BLI_str_format_byte_unit(size_str, ctx.bake->bake_size, true);
|
||||
if (ctx.bake->packed) {
|
||||
return fmt::format(RPT_("{} ({} packed)"), baked_str, size_str);
|
||||
}
|
||||
return fmt::format(RPT_("Baked {} - {}"), ctx.baked_range->first(), ctx.baked_range->last());
|
||||
return fmt::format(RPT_("{} ({} on disk)"), baked_str, size_str);
|
||||
}
|
||||
if (ctx.frame_range.has_value()) {
|
||||
if (!ctx.bake_still) {
|
||||
@@ -750,15 +762,21 @@ std::optional<std::string> get_bake_state_string(const BakeDrawContext &ctx)
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void draw_bake_button(const BakeDrawContext &ctx, uiLayout *layout)
|
||||
void draw_bake_button_row(const BakeDrawContext &ctx, uiLayout *layout, const bool is_in_sidebar)
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(layout, true);
|
||||
uiLayout *row = uiLayoutRow(col, true);
|
||||
{
|
||||
const char *bake_label = IFACE_("Bake");
|
||||
if (is_in_sidebar) {
|
||||
bake_label = ctx.bake_target == NODES_MODIFIER_BAKE_TARGET_DISK ? IFACE_("Bake to Disk") :
|
||||
IFACE_("Bake Packed");
|
||||
}
|
||||
|
||||
PointerRNA ptr;
|
||||
uiItemFullO(row,
|
||||
"OBJECT_OT_geometry_node_bake_single",
|
||||
IFACE_("Bake"),
|
||||
bake_label,
|
||||
ICON_NONE,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
@@ -771,34 +789,109 @@ void draw_bake_button(const BakeDrawContext &ctx, uiLayout *layout)
|
||||
{
|
||||
uiLayout *subrow = uiLayoutRow(row, true);
|
||||
uiLayoutSetActive(subrow, ctx.is_baked);
|
||||
PointerRNA ptr;
|
||||
uiItemFullO(subrow,
|
||||
"OBJECT_OT_geometry_node_bake_delete_single",
|
||||
"",
|
||||
ICON_TRASH,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
WM_operator_properties_id_lookup_set_from_id(&ptr, &ctx.object->id);
|
||||
RNA_string_set(&ptr, "modifier_name", ctx.nmd->modifier.name);
|
||||
RNA_int_set(&ptr, "bake_id", ctx.bake->id);
|
||||
if (is_in_sidebar) {
|
||||
if (ctx.is_baked && !G.is_rendering) {
|
||||
if (ctx.bake->packed) {
|
||||
PointerRNA ptr;
|
||||
uiItemFullO(subrow,
|
||||
"OBJECT_OT_geometry_node_bake_unpack_single",
|
||||
"",
|
||||
ICON_PACKAGE,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
WM_operator_properties_id_lookup_set_from_id(&ptr, &ctx.object->id);
|
||||
RNA_string_set(&ptr, "modifier_name", ctx.nmd->modifier.name);
|
||||
RNA_int_set(&ptr, "bake_id", ctx.bake->id);
|
||||
}
|
||||
else {
|
||||
PointerRNA ptr;
|
||||
uiItemFullO(subrow,
|
||||
"OBJECT_OT_geometry_node_bake_pack_single",
|
||||
"",
|
||||
ICON_UGLYPACKAGE,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
WM_operator_properties_id_lookup_set_from_id(&ptr, &ctx.object->id);
|
||||
RNA_string_set(&ptr, "modifier_name", ctx.nmd->modifier.name);
|
||||
RNA_int_set(&ptr, "bake_id", ctx.bake->id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If the data is not yet baked, still show the icon based on the derived bake target.*/
|
||||
const int icon = ctx.bake_target == NODES_MODIFIER_BAKE_TARGET_DISK ? ICON_UGLYPACKAGE :
|
||||
ICON_PACKAGE;
|
||||
PointerRNA ptr;
|
||||
uiItemFullO(subrow,
|
||||
"OBJECT_OT_geometry_node_bake_pack_single",
|
||||
"",
|
||||
icon,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
}
|
||||
}
|
||||
{
|
||||
PointerRNA ptr;
|
||||
uiItemFullO(subrow,
|
||||
"OBJECT_OT_geometry_node_bake_delete_single",
|
||||
"",
|
||||
ICON_TRASH,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
WM_operator_properties_id_lookup_set_from_id(&ptr, &ctx.object->id);
|
||||
RNA_string_set(&ptr, "modifier_name", ctx.nmd->modifier.name);
|
||||
RNA_int_set(&ptr, "bake_id", ctx.bake->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_common_bake_settings(BakeDrawContext &ctx, uiLayout *layout)
|
||||
void draw_common_bake_settings(bContext *C, BakeDrawContext &ctx, uiLayout *layout)
|
||||
{
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
uiLayoutSetPropDecorate(layout, false);
|
||||
|
||||
uiLayout *settings_col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetPropSep(settings_col, true);
|
||||
uiLayoutSetPropDecorate(settings_col, false);
|
||||
uiLayoutSetActive(settings_col, !ctx.is_baked);
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(settings_col, true);
|
||||
uiLayoutSetActive(col, !ctx.is_baked);
|
||||
uiItemR(col, &ctx.bake_rna, "use_custom_path", UI_ITEM_NONE, IFACE_("Custom Path"), ICON_NONE);
|
||||
uiItemR(col, &ctx.bake_rna, "bake_target", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiLayout *subcol = uiLayoutColumn(col, true);
|
||||
uiLayoutSetActive(subcol, ctx.bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH);
|
||||
uiItemR(subcol, &ctx.bake_rna, "directory", UI_ITEM_NONE, IFACE_("Path"), ICON_NONE);
|
||||
uiLayoutSetActive(subcol, ctx.bake_target == NODES_MODIFIER_BAKE_TARGET_DISK);
|
||||
uiItemR(
|
||||
subcol, &ctx.bake_rna, "use_custom_path", UI_ITEM_NONE, IFACE_("Custom Path"), ICON_NONE);
|
||||
uiLayout *subsubcol = uiLayoutColumn(subcol, true);
|
||||
const bool use_custom_path = ctx.bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH;
|
||||
uiLayoutSetActive(subsubcol, use_custom_path);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
auto bake_path = bke::bake::get_node_bake_path(*bmain, *ctx.object, *ctx.nmd, ctx.bake->id);
|
||||
|
||||
char placeholder_path[FILE_MAX] = "";
|
||||
if (StringRef(ctx.bake->directory).is_empty() &&
|
||||
!(ctx.bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH) && bake_path.has_value() &&
|
||||
bake_path->bake_dir.has_value())
|
||||
{
|
||||
STRNCPY(placeholder_path, bake_path->bake_dir->c_str());
|
||||
if (BLI_path_is_rel(ctx.nmd->bake_directory)) {
|
||||
BLI_path_rel(placeholder_path, BKE_main_blendfile_path(bmain));
|
||||
}
|
||||
}
|
||||
|
||||
uiItemFullR(subsubcol,
|
||||
&ctx.bake_rna,
|
||||
RNA_struct_find_property(&ctx.bake_rna, "directory"),
|
||||
-1,
|
||||
0,
|
||||
UI_ITEM_NONE,
|
||||
IFACE_("Path"),
|
||||
ICON_NONE,
|
||||
placeholder_path);
|
||||
}
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(settings_col, true);
|
||||
|
||||
@@ -327,12 +327,13 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_no
|
||||
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
draw_bake_button(ctx, col);
|
||||
draw_bake_button_row(ctx, col, true);
|
||||
if (const std::optional<std::string> bake_state_str = get_bake_state_string(ctx)) {
|
||||
uiItemL(col, bake_state_str->c_str(), ICON_NONE);
|
||||
uiLayout *row = uiLayoutRow(col, true);
|
||||
uiItemL(row, bake_state_str->c_str(), ICON_NONE);
|
||||
}
|
||||
}
|
||||
draw_common_bake_settings(ctx, layout);
|
||||
draw_common_bake_settings(C, ctx, layout);
|
||||
draw_data_blocks(C, layout, ctx.bake_rna);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user