Files
test2/source/blender/blenkernel/intern/bake_geometry_nodes_modifier.cc
Jacques Lucke 3ccfa65245 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
2024-09-20 16:18:12 +02:00

269 lines
8.2 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <sstream>
#include "BKE_bake_geometry_nodes_modifier.hh"
#include "BKE_collection.hh"
#include "BKE_main.hh"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "BLI_binary_search.hh"
#include "BLI_fileops.hh"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "MOD_nodes.hh"
namespace blender::bke::bake {
void SimulationNodeCache::reset()
{
std::destroy_at(this);
new (this) SimulationNodeCache();
}
void BakeNodeCache::reset()
{
std::destroy_at(this);
new (this) BakeNodeCache();
}
void NodeBakeCache::reset()
{
std::destroy_at(this);
new (this) NodeBakeCache();
}
IndexRange NodeBakeCache::frame_range() const
{
if (this->frames.is_empty()) {
return {};
}
const int start_frame = this->frames.first()->frame.frame();
const int end_frame = this->frames.last()->frame.frame();
return IndexRange::from_begin_end_inclusive(start_frame, end_frame);
}
SimulationNodeCache *ModifierCache::get_simulation_node_cache(const int id)
{
std::unique_ptr<SimulationNodeCache> *ptr = this->simulation_cache_by_id.lookup_ptr(id);
return ptr ? (*ptr).get() : nullptr;
}
BakeNodeCache *ModifierCache::get_bake_node_cache(const int id)
{
std::unique_ptr<BakeNodeCache> *ptr = this->bake_cache_by_id.lookup_ptr(id);
return ptr ? (*ptr).get() : nullptr;
}
NodeBakeCache *ModifierCache::get_node_bake_cache(const int id)
{
if (SimulationNodeCache *cache = this->get_simulation_node_cache(id)) {
return &cache->bake;
}
if (BakeNodeCache *cache = this->get_bake_node_cache(id)) {
return &cache->bake;
}
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) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type != eModifierType_Nodes) {
continue;
}
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (!nmd->runtime->cache) {
continue;
}
for (auto item : nmd->runtime->cache->simulation_cache_by_id.items()) {
item.value->reset();
}
}
}
FOREACH_SCENE_OBJECT_END;
}
std::optional<std::string> get_modifier_bake_path(const Main &bmain,
const Object &object,
const NodesModifierData &nmd)
{
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;
}
char absolute_bake_dir[FILE_MAX];
STRNCPY(absolute_bake_dir, nmd.bake_directory);
BLI_path_abs(absolute_bake_dir, base_path);
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,
int node_id)
{
const NodesModifierBake *bake = nmd.find_bake(node_id);
if (bake == nullptr) {
return std::nullopt;
}
if (bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH) {
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;
}
char absolute_bake_dir[FILE_MAX];
STRNCPY(absolute_bake_dir, bake->directory);
BLI_path_abs(absolute_bake_dir, base_path);
return bake::BakePath::from_single_root(absolute_bake_dir);
}
const std::optional<std::string> modifier_bake_path = get_modifier_bake_path(bmain, object, nmd);
if (!modifier_bake_path) {
return std::nullopt;
}
char bake_dir[FILE_MAX];
BLI_path_join(
bake_dir, sizeof(bake_dir), modifier_bake_path->c_str(), std::to_string(node_id).c_str());
return bake::BakePath::from_single_root(bake_dir);
}
static IndexRange fix_frame_range(const int start, const int end)
{
const int num_frames = std::max(1, end - start + 1);
return IndexRange(start, num_frames);
}
std::optional<IndexRange> get_node_bake_frame_range(const Scene &scene,
const Object & /*object*/,
const NodesModifierData &nmd,
int node_id)
{
const NodesModifierBake *bake = nmd.find_bake(node_id);
if (bake == nullptr) {
return std::nullopt;
}
if (bake->flag & NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE) {
return fix_frame_range(bake->frame_start, bake->frame_end);
}
if (scene.flag & SCE_CUSTOM_SIMULATION_RANGE) {
return fix_frame_range(scene.simulation_frame_start, scene.simulation_frame_end);
}
return fix_frame_range(scene.r.sfra, scene.r.efra);
}
/**
* Turn the name into something that can be used as file name. It does not necessarily have to be
* human readable, but it can help if it is at least partially readable.
*/
static std::string escape_name(const StringRef name)
{
std::stringstream ss;
for (const char c : name) {
/* Only some letters allowed. Digits are not because they could lead to name collisions. */
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) {
ss << c;
}
else {
ss << int(c);
}
}
return ss.str();
}
static std::string get_blend_file_name(const Main &bmain)
{
const StringRefNull blend_file_path = BKE_main_blendfile_path(&bmain);
char blend_name[FILE_MAX];
BLI_path_split_file_part(blend_file_path.c_str(), blend_name, sizeof(blend_name));
const int64_t type_start_index = StringRef(blend_name).rfind(".");
if (type_start_index == StringRef::not_found) {
return "";
}
blend_name[type_start_index] = '\0';
return "blendcache_" + StringRef(blend_name);
}
static std::string get_modifier_directory_name(const Object &object, const ModifierData &md)
{
const std::string object_name_escaped = escape_name(object.id.name + 2);
const std::string modifier_name_escaped = escape_name(md.name);
return object_name_escaped + "_" + modifier_name_escaped;
}
std::string get_default_modifier_bake_directory(const Main &bmain,
const Object &object,
const NodesModifierData &nmd)
{
char dir[FILE_MAX];
/* Make path that's relative to the .blend file. */
BLI_path_join(dir,
sizeof(dir),
"//",
get_blend_file_name(bmain).c_str(),
get_modifier_directory_name(object, nmd.modifier).c_str());
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