Geometry Nodes: support baking data block references
With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
This commit is contained in:
56
source/blender/blenkernel/BKE_bake_data_block_id.hh
Normal file
56
source/blender/blenkernel/BKE_bake_data_block_id.hh
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_struct_equality_utils.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
|
||||
struct ID;
|
||||
struct NodesModifierDataBlock;
|
||||
|
||||
namespace blender::bke::bake {
|
||||
|
||||
/**
|
||||
* Unique weak reference to a data block within a #Main. It's used when caching/baking data-block
|
||||
* references. Data-block pointers can't be used directly, because they are not stable over time
|
||||
* and between Blender sessions.
|
||||
*/
|
||||
struct BakeDataBlockID {
|
||||
ID_Type type;
|
||||
/**
|
||||
* Name of the data-block, without the type prefix.
|
||||
*/
|
||||
std::string id_name;
|
||||
/**
|
||||
* Name of the library data-block that the data-block is in. This refers to `Library.id.name` and
|
||||
* not the file path. The type prefix of the name is omitted. If this is empty, the data-block is
|
||||
* expected to be local and not linked.
|
||||
*/
|
||||
std::string lib_name;
|
||||
|
||||
BakeDataBlockID(ID_Type type, std::string id_name, std::string lib_name);
|
||||
BakeDataBlockID(const ID &id);
|
||||
BakeDataBlockID(const NodesModifierDataBlock &data_block);
|
||||
|
||||
uint64_t hash() const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const BakeDataBlockID &id);
|
||||
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_3(BakeDataBlockID, type, id_name, lib_name)
|
||||
};
|
||||
|
||||
/**
|
||||
* A list of weak data-block references for material slots.
|
||||
*/
|
||||
struct BakeMaterialsList : public Vector<std::optional<BakeDataBlockID>> {};
|
||||
|
||||
} // namespace blender::bke::bake
|
||||
43
source/blender/blenkernel/BKE_bake_data_block_map.hh
Normal file
43
source/blender/blenkernel/BKE_bake_data_block_map.hh
Normal file
@@ -0,0 +1,43 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_struct_equality_utils.hh"
|
||||
|
||||
#include "BKE_bake_data_block_id.hh"
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
|
||||
namespace blender::bke::bake {
|
||||
|
||||
/**
|
||||
* Maps #BakeDataBlockID to the corresponding data-blocks. This is used during depsgraph evaluation
|
||||
* to remap weak data-block references stored in baked data to the actual data-blocks at run-time.
|
||||
*
|
||||
* Also it keeps track of missing data-blocks, so that they can be added later.
|
||||
*/
|
||||
struct BakeDataBlockMap {
|
||||
public:
|
||||
/**
|
||||
* Tries to retrieve the data block for the given key. If it's not explicitly mapped, it might be
|
||||
* added to the mapping. If it's still not found, null is returned.
|
||||
*/
|
||||
virtual ID *lookup_or_remember_missing(const BakeDataBlockID &key) = 0;
|
||||
|
||||
/**
|
||||
* Tries to add the data block to the map. This may not succeed in all cases, e.g. if the
|
||||
* implementation does not allow inserting new mapping items.
|
||||
*/
|
||||
virtual void try_add(ID &id) = 0;
|
||||
};
|
||||
|
||||
} // namespace blender::bke::bake
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_bake_data_block_map.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
||||
namespace blender::bke::bake {
|
||||
@@ -42,13 +43,18 @@ class GeometryBakeItem : public BakeItem {
|
||||
GeometryBakeItem(GeometrySet geometry);
|
||||
|
||||
/**
|
||||
* Removes parts of the geometry that can't be stored in the simulation state:
|
||||
* - Anonymous attributes can't be stored because it is not known which of them will or will not
|
||||
* be used in the future.
|
||||
* - Materials can't be stored directly, because they are linked ID data blocks that can't be
|
||||
* restored from baked data currently.
|
||||
* Removes parts of the geometry that can't be baked/cached (anonymous attributes) and replaces
|
||||
* data-block pointers with #BakeDataBlockID.
|
||||
*/
|
||||
static void cleanup_geometry(GeometrySet &geometry);
|
||||
static void prepare_geometry_for_bake(GeometrySet &geometry, BakeDataBlockMap *data_block_map);
|
||||
|
||||
/**
|
||||
* The baked data does not have raw pointers to referenced data-blocks because those would become
|
||||
* dangling quickly. Instead it has weak name-based references (#BakeDataBlockID). This function
|
||||
* attempts to restore the actual data block pointers based on the weak references using the
|
||||
* given mapping.
|
||||
*/
|
||||
static void try_restore_data_blocks(GeometrySet &geometry, BakeDataBlockMap *data_block_map);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,8 +35,8 @@ struct BakeSocketConfig {
|
||||
* Create new bake items from the socket values. The socket values are not destructed, but they may
|
||||
* be in a moved-from state afterwards.
|
||||
*/
|
||||
Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(Span<void *> socket_values,
|
||||
const BakeSocketConfig &config);
|
||||
Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(
|
||||
Span<void *> socket_values, const BakeSocketConfig &config, BakeDataBlockMap *data_block_map);
|
||||
|
||||
/**
|
||||
* Create socket values from bake items.
|
||||
@@ -52,6 +52,7 @@ Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(Span<void *> s
|
||||
void move_bake_items_to_socket_values(
|
||||
Span<BakeItem *> bake_items,
|
||||
const BakeSocketConfig &config,
|
||||
BakeDataBlockMap *data_block_map,
|
||||
FunctionRef<std::shared_ptr<AnonymousAttributeFieldInput>(int socket_index, const CPPType &)>
|
||||
make_attribute_field,
|
||||
Span<void *> r_socket_values);
|
||||
@@ -63,6 +64,7 @@ void move_bake_items_to_socket_values(
|
||||
void copy_bake_items_to_socket_values(
|
||||
Span<const BakeItem *> bake_items,
|
||||
const BakeSocketConfig &config,
|
||||
BakeDataBlockMap *data_block_map,
|
||||
FunctionRef<std::shared_ptr<AnonymousAttributeFieldInput>(int, const CPPType &)>
|
||||
make_attribute_field,
|
||||
Span<void *> r_socket_values);
|
||||
|
||||
@@ -33,6 +33,9 @@ class AttributeAccessor;
|
||||
class MutableAttributeAccessor;
|
||||
enum class AttrDomain : int8_t;
|
||||
} // namespace blender::bke
|
||||
namespace blender::bke::bake {
|
||||
struct BakeMaterialsList;
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
@@ -111,6 +114,9 @@ class CurvesGeometryRuntime {
|
||||
|
||||
/** Normal direction vectors for each evaluated point. */
|
||||
mutable SharedCache<Vector<float3>> evaluated_normal_cache;
|
||||
|
||||
/** Stores weak references to material data blocks. */
|
||||
std::unique_ptr<bake::BakeMaterialsList> bake_materials;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -204,6 +204,10 @@ void BKE_libblock_ensure_unique_name(Main *bmain, ID *id) ATTR_NONNULL();
|
||||
ID *BKE_libblock_find_name(Main *bmain, short type, const char *name) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
ID *BKE_libblock_find_session_uid(Main *bmain, short type, uint32_t session_uid);
|
||||
ID *BKE_libblock_find_name_and_library(Main *bmain,
|
||||
short type,
|
||||
const char *name,
|
||||
const char *lib_name);
|
||||
/**
|
||||
* Duplicate (a.k.a. deep copy) common processing options.
|
||||
* See also eDupli_ID_Flags for options controlling what kind of IDs to duplicate.
|
||||
|
||||
@@ -29,6 +29,9 @@ struct SubsurfRuntimeData;
|
||||
namespace blender::bke {
|
||||
struct EditMeshData;
|
||||
}
|
||||
namespace blender::bke::bake {
|
||||
struct BakeMaterialsList;
|
||||
}
|
||||
|
||||
/** #MeshRuntime.wrapper_type */
|
||||
enum eMeshWrapperType {
|
||||
@@ -207,6 +210,9 @@ struct MeshRuntime {
|
||||
*/
|
||||
BitVector<> subsurf_optimal_display_edges;
|
||||
|
||||
/** Stores weak references to material data blocks. */
|
||||
std::unique_ptr<bake::BakeMaterialsList> bake_materials;
|
||||
|
||||
MeshRuntime();
|
||||
~MeshRuntime();
|
||||
};
|
||||
|
||||
@@ -22,6 +22,9 @@ struct Main;
|
||||
struct Object;
|
||||
struct PointCloud;
|
||||
struct Scene;
|
||||
namespace blender::bke::bake {
|
||||
struct BakeMaterialsList;
|
||||
}
|
||||
|
||||
/* PointCloud datablock */
|
||||
extern const char *POINTCLOUD_ATTR_POSITION;
|
||||
@@ -37,6 +40,9 @@ struct PointCloudRuntime {
|
||||
*/
|
||||
mutable SharedCache<Bounds<float3>> bounds_cache;
|
||||
|
||||
/** Stores weak references to material data blocks. */
|
||||
std::unique_ptr<bake::BakeMaterialsList> bake_materials;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("PointCloudRuntime");
|
||||
};
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ set(SRC
|
||||
intern/attribute_math.cc
|
||||
intern/autoexec.cc
|
||||
intern/bake_geometry_nodes_modifier.cc
|
||||
intern/bake_data_block_map.cc
|
||||
intern/bake_items.cc
|
||||
intern/bake_items_paths.cc
|
||||
intern/bake_items_serialize.cc
|
||||
@@ -336,6 +337,7 @@ set(SRC
|
||||
BKE_attribute_math.hh
|
||||
BKE_autoexec.hh
|
||||
BKE_bake_geometry_nodes_modifier.hh
|
||||
BKE_bake_data_block_map.hh
|
||||
BKE_bake_items.hh
|
||||
BKE_bake_items_paths.hh
|
||||
BKE_bake_items_serialize.hh
|
||||
|
||||
47
source/blender/blenkernel/intern/bake_data_block_map.cc
Normal file
47
source/blender/blenkernel/intern/bake_data_block_map.cc
Normal file
@@ -0,0 +1,47 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
|
||||
#include "BKE_bake_data_block_map.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
namespace blender::bke::bake {
|
||||
|
||||
BakeDataBlockID::BakeDataBlockID(ID_Type type, std::string id_name, std::string lib_name)
|
||||
: type(type), id_name(std::move(id_name)), lib_name(std::move(lib_name))
|
||||
{
|
||||
}
|
||||
|
||||
BakeDataBlockID::BakeDataBlockID(const ID &id)
|
||||
{
|
||||
this->type = GS(id.name);
|
||||
this->id_name = id.name + 2;
|
||||
if (id.lib) {
|
||||
this->lib_name = id.lib->id.name + 2;
|
||||
}
|
||||
}
|
||||
|
||||
BakeDataBlockID::BakeDataBlockID(const NodesModifierDataBlock &data_block)
|
||||
: BakeDataBlockID(ID_Type(data_block.id_type),
|
||||
StringRef(data_block.id_name),
|
||||
StringRef(data_block.lib_name))
|
||||
{
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const BakeDataBlockID &id)
|
||||
{
|
||||
return stream << "(" << id.id_name << ", Lib: " << id.lib_name << ")";
|
||||
}
|
||||
|
||||
uint64_t BakeDataBlockID::hash() const
|
||||
{
|
||||
return get_default_hash(this->type, this->id_name, this->lib_name);
|
||||
}
|
||||
|
||||
} // namespace blender::bke::bake
|
||||
@@ -27,27 +27,49 @@ using DictionaryValuePtr = std::shared_ptr<DictionaryValue>;
|
||||
|
||||
GeometryBakeItem::GeometryBakeItem(GeometrySet geometry) : geometry(std::move(geometry)) {}
|
||||
|
||||
static void remove_materials(Material ***materials, short *materials_num)
|
||||
static std::unique_ptr<BakeMaterialsList> materials_to_weak_references(
|
||||
Material ***materials, short *materials_num, BakeDataBlockMap *data_block_map)
|
||||
{
|
||||
if (*materials_num == 0) {
|
||||
return {};
|
||||
}
|
||||
auto materials_list = std::make_unique<BakeMaterialsList>();
|
||||
materials_list->resize(*materials_num);
|
||||
for (const int i : materials_list->index_range()) {
|
||||
Material *material = (*materials)[i];
|
||||
if (material) {
|
||||
(*materials_list)[i] = BakeDataBlockID(material->id);
|
||||
if (data_block_map) {
|
||||
data_block_map->try_add(material->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(*materials);
|
||||
*materials_num = 0;
|
||||
|
||||
return materials_list;
|
||||
}
|
||||
|
||||
void GeometryBakeItem::cleanup_geometry(GeometrySet &main_geometry)
|
||||
void GeometryBakeItem::prepare_geometry_for_bake(GeometrySet &main_geometry,
|
||||
BakeDataBlockMap *data_block_map)
|
||||
{
|
||||
main_geometry.ensure_owns_all_data();
|
||||
main_geometry.modify_geometry_sets([&](GeometrySet &geometry) {
|
||||
if (Mesh *mesh = geometry.get_mesh_for_write()) {
|
||||
mesh->attributes_for_write().remove_anonymous();
|
||||
remove_materials(&mesh->mat, &mesh->totcol);
|
||||
mesh->runtime->bake_materials = materials_to_weak_references(
|
||||
&mesh->mat, &mesh->totcol, data_block_map);
|
||||
}
|
||||
if (Curves *curves = geometry.get_curves_for_write()) {
|
||||
curves->geometry.wrap().attributes_for_write().remove_anonymous();
|
||||
remove_materials(&curves->mat, &curves->totcol);
|
||||
curves->geometry.runtime->bake_materials = materials_to_weak_references(
|
||||
&curves->mat, &curves->totcol, data_block_map);
|
||||
}
|
||||
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
|
||||
pointcloud->attributes_for_write().remove_anonymous();
|
||||
remove_materials(&pointcloud->mat, &pointcloud->totcol);
|
||||
pointcloud->runtime->bake_materials = materials_to_weak_references(
|
||||
&pointcloud->mat, &pointcloud->totcol, data_block_map);
|
||||
}
|
||||
if (bke::Instances *instances = geometry.get_instances_for_write()) {
|
||||
instances->attributes_for_write().remove_anonymous();
|
||||
@@ -59,6 +81,53 @@ void GeometryBakeItem::cleanup_geometry(GeometrySet &main_geometry)
|
||||
});
|
||||
}
|
||||
|
||||
static void restore_materials(Material ***materials,
|
||||
short *materials_num,
|
||||
std::unique_ptr<BakeMaterialsList> materials_list,
|
||||
BakeDataBlockMap *data_block_map)
|
||||
{
|
||||
if (!materials_list) {
|
||||
return;
|
||||
}
|
||||
BLI_assert(*materials == nullptr);
|
||||
*materials_num = materials_list->size();
|
||||
*materials = MEM_cnew_array<Material *>(materials_list->size(), __func__);
|
||||
if (!data_block_map) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const int i : materials_list->index_range()) {
|
||||
const std::optional<BakeDataBlockID> &data_block_id = (*materials_list)[i];
|
||||
if (data_block_id) {
|
||||
(*materials)[i] = reinterpret_cast<Material *>(
|
||||
data_block_map->lookup_or_remember_missing(*data_block_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryBakeItem::try_restore_data_blocks(GeometrySet &main_geometry,
|
||||
BakeDataBlockMap *data_block_map)
|
||||
{
|
||||
main_geometry.modify_geometry_sets([&](GeometrySet &geometry) {
|
||||
if (Mesh *mesh = geometry.get_mesh_for_write()) {
|
||||
restore_materials(
|
||||
&mesh->mat, &mesh->totcol, std::move(mesh->runtime->bake_materials), data_block_map);
|
||||
}
|
||||
if (Curves *curves = geometry.get_curves_for_write()) {
|
||||
restore_materials(&curves->mat,
|
||||
&curves->totcol,
|
||||
std::move(curves->geometry.runtime->bake_materials),
|
||||
data_block_map);
|
||||
}
|
||||
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
|
||||
restore_materials(&pointcloud->mat,
|
||||
&pointcloud->totcol,
|
||||
std::move(pointcloud->runtime->bake_materials),
|
||||
data_block_map);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
PrimitiveBakeItem::PrimitiveBakeItem(const CPPType &type, const void *value) : type_(type)
|
||||
{
|
||||
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
|
||||
|
||||
@@ -365,6 +365,32 @@ template<typename T>
|
||||
return *r_data != nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool load_materials(const io::serialize::ArrayValue &io_materials,
|
||||
std::unique_ptr<BakeMaterialsList> &materials)
|
||||
{
|
||||
if (io_materials.elements().is_empty()) {
|
||||
return true;
|
||||
}
|
||||
materials = std::make_unique<BakeMaterialsList>();
|
||||
for (const auto &io_material_value : io_materials.elements()) {
|
||||
if (io_material_value->type() == io::serialize::eValueType::Null) {
|
||||
materials->append(std::nullopt);
|
||||
continue;
|
||||
}
|
||||
const auto *io_material = io_material_value->as_dictionary_value();
|
||||
if (!io_material) {
|
||||
return false;
|
||||
}
|
||||
std::optional<std::string> id_name = io_material->lookup_str("name");
|
||||
if (!id_name) {
|
||||
return false;
|
||||
}
|
||||
std::string lib_name = io_material->lookup_str("lib_name").value_or("");
|
||||
materials->append(BakeDataBlockID(ID_MA, std::move(*id_name), std::move(lib_name)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool load_attributes(const io::serialize::ArrayValue &io_attributes,
|
||||
MutableAttributeAccessor &attributes,
|
||||
const BlobReader &blob_reader,
|
||||
@@ -450,6 +476,12 @@ static PointCloud *try_load_pointcloud(const DictionaryValue &io_geometry,
|
||||
if (!load_attributes(*io_attributes, attributes, blob_reader, blob_sharing)) {
|
||||
return cancel();
|
||||
}
|
||||
|
||||
if (const io::serialize::ArrayValue *io_materials = io_pointcloud->lookup_array("materials")) {
|
||||
if (!load_materials(*io_materials, pointcloud->runtime->bake_materials)) {
|
||||
return cancel();
|
||||
}
|
||||
}
|
||||
return pointcloud;
|
||||
}
|
||||
|
||||
@@ -499,6 +531,12 @@ static Curves *try_load_curves(const DictionaryValue &io_geometry,
|
||||
return cancel();
|
||||
}
|
||||
|
||||
if (const io::serialize::ArrayValue *io_materials = io_curves->lookup_array("materials")) {
|
||||
if (!load_materials(*io_materials, curves.runtime->bake_materials)) {
|
||||
return cancel();
|
||||
}
|
||||
}
|
||||
|
||||
curves.update_curve_types();
|
||||
|
||||
return curves_id;
|
||||
@@ -554,6 +592,12 @@ static Mesh *try_load_mesh(const DictionaryValue &io_geometry,
|
||||
return cancel();
|
||||
}
|
||||
|
||||
if (const io::serialize::ArrayValue *io_materials = io_mesh->lookup_array("materials")) {
|
||||
if (!load_materials(*io_materials, mesh->runtime->bake_materials)) {
|
||||
return cancel();
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
@@ -630,20 +674,23 @@ static GeometrySet load_geometry(const DictionaryValue &io_geometry,
|
||||
return geometry;
|
||||
}
|
||||
|
||||
static std::shared_ptr<io::serialize::ArrayValue> serialize_material_slots(
|
||||
const Span<const Material *> material_slots)
|
||||
static std::shared_ptr<io::serialize::ArrayValue> serialize_materials(
|
||||
const std::unique_ptr<BakeMaterialsList> &materials)
|
||||
{
|
||||
auto io_materials = std::make_shared<io::serialize::ArrayValue>();
|
||||
for (const Material *material : material_slots) {
|
||||
if (material == nullptr) {
|
||||
io_materials->append_null();
|
||||
if (!materials) {
|
||||
return io_materials;
|
||||
}
|
||||
for (const std::optional<BakeDataBlockID> &material : *materials) {
|
||||
if (material) {
|
||||
auto io_material = io_materials->append_dict();
|
||||
io_material->append_str("name", material->id_name);
|
||||
if (!material->lib_name.empty()) {
|
||||
io_material->append_str("lib_name", material->lib_name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto io_material = io_materials->append_dict();
|
||||
io_material->append_str("name", material->id.name + 2);
|
||||
if (material->id.lib != nullptr) {
|
||||
io_material->append_str("lib_name", material->id.lib->id.name + 2);
|
||||
}
|
||||
io_materials->append_null();
|
||||
}
|
||||
}
|
||||
return io_materials;
|
||||
@@ -707,7 +754,7 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
|
||||
mesh.runtime->face_offsets_sharing_info));
|
||||
}
|
||||
|
||||
auto io_materials = serialize_material_slots({mesh.mat, mesh.totcol});
|
||||
auto io_materials = serialize_materials(mesh.runtime->bake_materials);
|
||||
io_mesh->append("materials", io_materials);
|
||||
|
||||
auto io_attributes = serialize_attributes(mesh.attributes(), blob_writer, blob_sharing, {});
|
||||
@@ -719,7 +766,7 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
|
||||
|
||||
io_pointcloud->append_int("num_points", pointcloud.totpoint);
|
||||
|
||||
auto io_materials = serialize_material_slots({pointcloud.mat, pointcloud.totcol});
|
||||
auto io_materials = serialize_materials(pointcloud.runtime->bake_materials);
|
||||
io_pointcloud->append("materials", io_materials);
|
||||
|
||||
auto io_attributes = serialize_attributes(
|
||||
@@ -744,7 +791,7 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
|
||||
curves.runtime->curve_offsets_sharing_info));
|
||||
}
|
||||
|
||||
auto io_materials = serialize_material_slots({curves_id.mat, curves_id.totcol});
|
||||
auto io_materials = serialize_materials(curves.runtime->bake_materials);
|
||||
io_curves->append("materials", io_materials);
|
||||
|
||||
auto io_attributes = serialize_attributes(curves.attributes(), blob_writer, blob_sharing, {});
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_bake_items_socket.hh"
|
||||
|
||||
#include "BKE_geometry_fields.hh"
|
||||
#include "BKE_node.hh"
|
||||
|
||||
#include "BKE_node_socket_value.hh"
|
||||
|
||||
namespace blender::bke::bake {
|
||||
|
||||
Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(const Span<void *> socket_values,
|
||||
const BakeSocketConfig &config)
|
||||
const BakeSocketConfig &config,
|
||||
BakeDataBlockMap *data_block_map)
|
||||
{
|
||||
BLI_assert(socket_values.size() == config.types.size());
|
||||
BLI_assert(socket_values.size() == config.geometries_by_attribute.size());
|
||||
@@ -99,7 +98,7 @@ Array<std::unique_ptr<BakeItem>> move_socket_values_to_bake_items(const Span<voi
|
||||
continue;
|
||||
}
|
||||
GeometrySet &geometry = static_cast<GeometryBakeItem *>(bake_items[i].get())->geometry;
|
||||
GeometryBakeItem::cleanup_geometry(geometry);
|
||||
GeometryBakeItem::prepare_geometry_for_bake(geometry, data_block_map);
|
||||
}
|
||||
|
||||
return bake_items;
|
||||
@@ -192,6 +191,14 @@ static void rename_attributes(const Span<GeometrySet *> geometries,
|
||||
}
|
||||
}
|
||||
|
||||
static void restore_data_blocks(const Span<GeometrySet *> geometries,
|
||||
BakeDataBlockMap *data_block_map)
|
||||
{
|
||||
for (GeometrySet *main_geometry : geometries) {
|
||||
GeometryBakeItem::try_restore_data_blocks(*main_geometry, data_block_map);
|
||||
}
|
||||
}
|
||||
|
||||
static void default_initialize_socket_value(const eNodeSocketDatatype socket_type, void *r_value)
|
||||
{
|
||||
const char *socket_idname = nodeStaticSocketType(socket_type, 0);
|
||||
@@ -203,6 +210,7 @@ static void default_initialize_socket_value(const eNodeSocketDatatype socket_typ
|
||||
void move_bake_items_to_socket_values(
|
||||
const Span<BakeItem *> bake_items,
|
||||
const BakeSocketConfig &config,
|
||||
BakeDataBlockMap *data_block_map,
|
||||
FunctionRef<std::shared_ptr<AnonymousAttributeFieldInput>(int, const CPPType &)>
|
||||
make_attribute_field,
|
||||
const Span<void *> r_socket_values)
|
||||
@@ -237,11 +245,13 @@ void move_bake_items_to_socket_values(
|
||||
}
|
||||
|
||||
rename_attributes(geometries, attribute_map);
|
||||
restore_data_blocks(geometries, data_block_map);
|
||||
}
|
||||
|
||||
void copy_bake_items_to_socket_values(
|
||||
const Span<const BakeItem *> bake_items,
|
||||
const BakeSocketConfig &config,
|
||||
BakeDataBlockMap *data_block_map,
|
||||
FunctionRef<std::shared_ptr<AnonymousAttributeFieldInput>(int, const CPPType &)>
|
||||
make_attribute_field,
|
||||
const Span<void *> r_socket_values)
|
||||
@@ -273,6 +283,7 @@ void copy_bake_items_to_socket_values(
|
||||
}
|
||||
|
||||
rename_attributes(geometries, attribute_map);
|
||||
restore_data_blocks(geometries, data_block_map);
|
||||
}
|
||||
|
||||
} // namespace blender::bke::bake
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_bake_data_block_id.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_curves_utils.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
@@ -119,6 +120,11 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
|
||||
dst.runtime->evaluated_length_cache = src.runtime->evaluated_length_cache;
|
||||
dst.runtime->evaluated_tangent_cache = src.runtime->evaluated_tangent_cache;
|
||||
dst.runtime->evaluated_normal_cache = src.runtime->evaluated_normal_cache;
|
||||
|
||||
if (src.runtime->bake_materials) {
|
||||
dst.runtime->bake_materials = std::make_unique<bake::BakeMaterialsList>(
|
||||
*src.runtime->bake_materials);
|
||||
}
|
||||
}
|
||||
|
||||
CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) : CurvesGeometry()
|
||||
|
||||
@@ -1489,6 +1489,31 @@ ID *BKE_libblock_find_session_uid(Main *bmain, const short type, const uint32_t
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ID *BKE_libblock_find_name_and_library(Main *bmain,
|
||||
const short type,
|
||||
const char *name,
|
||||
const char *lib_name)
|
||||
{
|
||||
ListBase *lb = which_libbase(bmain, type);
|
||||
BLI_assert(lb != nullptr);
|
||||
LISTBASE_FOREACH (ID *, id, lb) {
|
||||
if (!STREQ(id->name + 2, name)) {
|
||||
continue;
|
||||
}
|
||||
if (lib_name == nullptr || lib_name[0] == '\0') {
|
||||
if (id->lib == nullptr) {
|
||||
return id;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
if (!STREQ(id->lib->id.name + 2, lib_name)) {
|
||||
continue;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
|
||||
{
|
||||
#define ID_SORT_STEP_SIZE 512
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
|
||||
#include "BKE_anim_data.h"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_bake_data_block_id.hh"
|
||||
#include "BKE_bpath.h"
|
||||
#include "BKE_deform.hh"
|
||||
#include "BKE_editmesh.hh"
|
||||
@@ -146,6 +147,10 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
|
||||
mesh_dst->runtime->vert_to_face_map_cache = mesh_src->runtime->vert_to_face_map_cache;
|
||||
mesh_dst->runtime->vert_to_corner_map_cache = mesh_src->runtime->vert_to_corner_map_cache;
|
||||
mesh_dst->runtime->corner_to_face_map_cache = mesh_src->runtime->corner_to_face_map_cache;
|
||||
if (mesh_src->runtime->bake_materials) {
|
||||
mesh_dst->runtime->bake_materials = std::make_unique<blender::bke::bake::BakeMaterialsList>(
|
||||
*mesh_src->runtime->bake_materials);
|
||||
}
|
||||
|
||||
/* Only do tessface if we have no faces. */
|
||||
const bool do_tessface = ((mesh_src->totface_legacy != 0) && (mesh_src->faces_num == 0));
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
#include "BKE_bake_data_block_id.hh"
|
||||
#include "BKE_bvhutils.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_editmesh_cache.hh"
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_anim_data.h"
|
||||
#include "BKE_bake_data_block_id.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_global.h"
|
||||
@@ -86,6 +87,11 @@ static void pointcloud_copy_data(Main * /*bmain*/,
|
||||
|
||||
pointcloud_dst->runtime = new blender::bke::PointCloudRuntime();
|
||||
pointcloud_dst->runtime->bounds_cache = pointcloud_src->runtime->bounds_cache;
|
||||
if (pointcloud_src->runtime->bake_materials) {
|
||||
pointcloud_dst->runtime->bake_materials =
|
||||
std::make_unique<blender::bke::bake::BakeMaterialsList>(
|
||||
*pointcloud_src->runtime->bake_materials);
|
||||
}
|
||||
|
||||
pointcloud_dst->batch_cache = nullptr;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "DNA_mask_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_rigidbody_types.h"
|
||||
@@ -79,6 +80,7 @@
|
||||
#include "BKE_linestyle.h"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_mask.h"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_object.hh"
|
||||
@@ -97,6 +99,7 @@
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
#include "DEG_depsgraph_debug.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
#include "DEG_depsgraph_writeback_sync.hh"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
@@ -2567,7 +2570,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
|
||||
prepare_mesh_for_viewport_render(bmain, scene, view_layer);
|
||||
/* Update all objects: drivers, matrices, etc. flags set
|
||||
* by depsgraph or manual, no layer check here, gets correct flushed. */
|
||||
DEG_evaluate_on_refresh(depsgraph);
|
||||
DEG_evaluate_on_refresh(depsgraph, DEG_EVALUATE_SYNC_WRITEBACK_YES);
|
||||
/* Update sound system. */
|
||||
BKE_scene_update_sound(depsgraph, bmain);
|
||||
/* Notify python about depsgraph update. */
|
||||
@@ -2647,10 +2650,10 @@ void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool cle
|
||||
* lose any possible unkeyed changes made by the handler. */
|
||||
if (pass == 0) {
|
||||
const float frame = BKE_scene_frame_get(scene);
|
||||
DEG_evaluate_on_framechange(depsgraph, frame);
|
||||
DEG_evaluate_on_framechange(depsgraph, frame, DEG_EVALUATE_SYNC_WRITEBACK_YES);
|
||||
}
|
||||
else {
|
||||
DEG_evaluate_on_refresh(depsgraph);
|
||||
DEG_evaluate_on_refresh(depsgraph, DEG_EVALUATE_SYNC_WRITEBACK_YES);
|
||||
}
|
||||
/* Update sound system animation. */
|
||||
BKE_scene_update_sound(depsgraph, bmain);
|
||||
|
||||
@@ -88,6 +88,7 @@ set(SRC
|
||||
intern/depsgraph_tag.cc
|
||||
intern/depsgraph_type.cc
|
||||
intern/depsgraph_update.cc
|
||||
intern/depsgraph_writeback_sync.cc
|
||||
|
||||
DEG_depsgraph.hh
|
||||
DEG_depsgraph_build.hh
|
||||
@@ -95,6 +96,7 @@ set(SRC
|
||||
DEG_depsgraph_light_linking.hh
|
||||
DEG_depsgraph_physics.hh
|
||||
DEG_depsgraph_query.hh
|
||||
DEG_depsgraph_writeback_sync.hh
|
||||
|
||||
intern/builder/deg_builder.h
|
||||
intern/builder/deg_builder_cache.h
|
||||
|
||||
@@ -173,18 +173,32 @@ void DEG_ids_restore_recalc(Depsgraph *depsgraph);
|
||||
/** \name Graph Evaluation
|
||||
* \{ */
|
||||
|
||||
enum DepsgraphEvaluateSyncWriteback {
|
||||
DEG_EVALUATE_SYNC_WRITEBACK_NO,
|
||||
/**
|
||||
* Allow writing back to original data after depsgraph evaluation. The change to original data
|
||||
* may add new ID relations and may tag the depsgraph as changed again.
|
||||
*/
|
||||
DEG_EVALUATE_SYNC_WRITEBACK_YES,
|
||||
};
|
||||
|
||||
/**
|
||||
* Frame changed recalculation entry point.
|
||||
*
|
||||
* \note The frame-change happened for root scene that graph belongs to.
|
||||
*/
|
||||
void DEG_evaluate_on_framechange(Depsgraph *graph, float frame);
|
||||
void DEG_evaluate_on_framechange(
|
||||
Depsgraph *graph,
|
||||
float frame,
|
||||
DepsgraphEvaluateSyncWriteback sync_writeback = DEG_EVALUATE_SYNC_WRITEBACK_NO);
|
||||
|
||||
/**
|
||||
* Data changed recalculation entry point.
|
||||
* Evaluate all nodes tagged for updating.
|
||||
*/
|
||||
void DEG_evaluate_on_refresh(Depsgraph *graph);
|
||||
void DEG_evaluate_on_refresh(
|
||||
Depsgraph *graph,
|
||||
DepsgraphEvaluateSyncWriteback sync_writeback = DEG_EVALUATE_SYNC_WRITEBACK_NO);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
31
source/blender/depsgraph/DEG_depsgraph_writeback_sync.hh
Normal file
31
source/blender/depsgraph/DEG_depsgraph_writeback_sync.hh
Normal file
@@ -0,0 +1,31 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*
|
||||
* This file provides an API that can be used to modify original (as opposed to evaluated)
|
||||
* data-blocks after depsgraph evaluation. For some data (e.g. animated properties), this is done
|
||||
* during depsgraph evaluation. However, this is not possible in all cases. For example, if the
|
||||
* change to the original data adds a new relation between data-blocks, a user-count (#ID.us) has
|
||||
* to be increased. This counter is not atomic and can therefore not be modified arbitrarily from
|
||||
* different threads.
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
namespace blender::deg::sync_writeback {
|
||||
|
||||
/**
|
||||
* Add a writeback task during depsgraph evaluation. The given function is called after depsgraph
|
||||
* evaluation is done if the depsgraph is active. It is allowed to change original data blocks and
|
||||
* even to add new relations.
|
||||
*/
|
||||
void add(Depsgraph &depsgraph, std::function<void()> fn);
|
||||
|
||||
} // namespace blender::deg::sync_writeback
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
@@ -174,6 +176,14 @@ struct Depsgraph {
|
||||
/* The number of times this graph has been evaluated. */
|
||||
uint64_t update_count;
|
||||
|
||||
/**
|
||||
* Stores functions that can be called after depsgraph evaluation to writeback some changes to
|
||||
* original data. Also see `DEG_depsgraph_writeback_sync.hh`.
|
||||
*/
|
||||
Vector<std::function<void()>> sync_writeback_callbacks;
|
||||
/** Needs to be locked when adding a writeback callback during evaluation. */
|
||||
std::mutex sync_writeback_callbacks_mutex;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Depsgraph");
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
#include "DEG_depsgraph_writeback_sync.hh"
|
||||
|
||||
#include "intern/eval/deg_eval.h"
|
||||
#include "intern/eval/deg_eval_flush.h"
|
||||
@@ -33,7 +34,8 @@
|
||||
|
||||
namespace deg = blender::deg;
|
||||
|
||||
static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph)
|
||||
static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph,
|
||||
const DepsgraphEvaluateSyncWriteback sync_writeback)
|
||||
{
|
||||
/* Update the time on the cow scene. */
|
||||
if (deg_graph->scene_cow) {
|
||||
@@ -43,9 +45,18 @@ static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph)
|
||||
deg::graph_tag_ids_for_visible_update(deg_graph);
|
||||
deg::deg_graph_flush_updates(deg_graph);
|
||||
deg::deg_evaluate_on_refresh(deg_graph);
|
||||
|
||||
if (sync_writeback == DEG_EVALUATE_SYNC_WRITEBACK_YES) {
|
||||
if (deg_graph->is_active) {
|
||||
for (std::function<void()> &fn : deg_graph->sync_writeback_callbacks) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
deg_graph->sync_writeback_callbacks.clear();
|
||||
}
|
||||
|
||||
void DEG_evaluate_on_refresh(Depsgraph *graph)
|
||||
void DEG_evaluate_on_refresh(Depsgraph *graph, const DepsgraphEvaluateSyncWriteback sync_writeback)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
|
||||
const Scene *scene = DEG_get_input_scene(graph);
|
||||
@@ -66,10 +77,12 @@ void DEG_evaluate_on_refresh(Depsgraph *graph)
|
||||
deg_graph->tag_time_source();
|
||||
}
|
||||
|
||||
deg_flush_updates_and_refresh(deg_graph);
|
||||
deg_flush_updates_and_refresh(deg_graph, sync_writeback);
|
||||
}
|
||||
|
||||
void DEG_evaluate_on_framechange(Depsgraph *graph, float frame)
|
||||
void DEG_evaluate_on_framechange(Depsgraph *graph,
|
||||
float frame,
|
||||
const DepsgraphEvaluateSyncWriteback sync_writeback)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
|
||||
const Scene *scene = DEG_get_input_scene(graph);
|
||||
@@ -77,5 +90,5 @@ void DEG_evaluate_on_framechange(Depsgraph *graph, float frame)
|
||||
deg_graph->tag_time_source();
|
||||
deg_graph->frame = frame;
|
||||
deg_graph->ctime = BKE_scene_frame_to_ctime(scene, frame);
|
||||
deg_flush_updates_and_refresh(deg_graph);
|
||||
deg_flush_updates_and_refresh(deg_graph, sync_writeback);
|
||||
}
|
||||
|
||||
27
source/blender/depsgraph/intern/depsgraph_writeback_sync.cc
Normal file
27
source/blender/depsgraph/intern/depsgraph_writeback_sync.cc
Normal file
@@ -0,0 +1,27 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_writeback_sync.hh"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "depsgraph.hh"
|
||||
|
||||
namespace blender::deg::sync_writeback {
|
||||
|
||||
void add(::Depsgraph &depsgraph, std::function<void()> fn)
|
||||
{
|
||||
deg::Depsgraph °_graph = reinterpret_cast<deg::Depsgraph &>(depsgraph);
|
||||
if (!deg_graph.is_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock{deg_graph.sync_writeback_callbacks_mutex};
|
||||
deg_graph.sync_writeback_callbacks.append(std::move(fn));
|
||||
}
|
||||
|
||||
} // namespace blender::deg::sync_writeback
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "DNA_array_utils.hh"
|
||||
#include "DNA_curves_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
@@ -706,6 +707,17 @@ static void try_delete_bake(
|
||||
else if (auto *node_cache = modifier_cache.bake_cache_by_id.lookup_ptr(bake_id)) {
|
||||
(*node_cache)->reset();
|
||||
}
|
||||
NodesModifierBake *bake = nmd.find_bake(bake_id);
|
||||
if (!bake) {
|
||||
return;
|
||||
}
|
||||
dna::array::clear<NodesModifierDataBlock>(&bake->data_blocks,
|
||||
&bake->data_blocks_num,
|
||||
&bake->active_data_block,
|
||||
[](NodesModifierDataBlock *data_block) {
|
||||
nodes_modifier_data_block_destruct(data_block, true);
|
||||
});
|
||||
|
||||
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
|
||||
*bmain, object, nmd, bake_id);
|
||||
if (!bake_path) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_array_utils.hh"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_dynamicpaint_types.h"
|
||||
#include "DNA_fluid_types.h"
|
||||
|
||||
@@ -2342,6 +2342,31 @@ typedef struct NodesModifierSettings {
|
||||
struct IDProperty *properties;
|
||||
} NodesModifierSettings;
|
||||
|
||||
/**
|
||||
* Maps a name (+ optional library name) to a data-block. The name can be stored on disk and is
|
||||
* remapped to the data-block when the data is loaded.
|
||||
*
|
||||
* At run-time, #BakeDataBlockID is used to pair up the data-block and library name.
|
||||
*/
|
||||
typedef struct NodesModifierDataBlock {
|
||||
/**
|
||||
* Name of the data-block. Can be empty in which case the name of the `id` below is used.
|
||||
* This only needs to be set manually when the name stored on disk does not exist in the .blend
|
||||
* file anymore, because e.g. the ID has been renamed.
|
||||
*/
|
||||
char *id_name;
|
||||
/**
|
||||
* Name of the library the ID is in. Can be empty when the ID is not linked or when `id_name` is
|
||||
* empty as well and thus the names from the `id` below are used.
|
||||
*/
|
||||
char *lib_name;
|
||||
/** ID that this is mapped to. */
|
||||
struct ID *id;
|
||||
/** Type of ID that is referenced by this mapping. */
|
||||
int id_type;
|
||||
char _pad[4];
|
||||
} NodesModifierDataBlock;
|
||||
|
||||
typedef struct NodesModifierBake {
|
||||
/** An id that references a nested node in the node tree. Also see #bNestedNodeRef. */
|
||||
int id;
|
||||
@@ -2361,6 +2386,17 @@ typedef struct NodesModifierBake {
|
||||
*/
|
||||
int frame_start;
|
||||
int frame_end;
|
||||
|
||||
/**
|
||||
* Maps data-block names to actual data-blocks, so that names stored in caches or on disk can be
|
||||
* remapped to actual IDs on load. The mapping also makes sure that IDs referenced by baked data
|
||||
* are not automatically removed because they are not referenced anymore. Furthermore, it allows
|
||||
* the modifier to add all required IDs to the dependency graph before actually loading the baked
|
||||
* data.
|
||||
*/
|
||||
int data_blocks_num;
|
||||
int active_data_block;
|
||||
NodesModifierDataBlock *data_blocks;
|
||||
} NodesModifierBake;
|
||||
|
||||
typedef struct NodesModifierPanel {
|
||||
@@ -2398,6 +2434,7 @@ typedef struct NodesModifierData {
|
||||
char _pad[3];
|
||||
int bakes_num;
|
||||
NodesModifierBake *bakes;
|
||||
|
||||
char _pad2[4];
|
||||
int panels_num;
|
||||
NodesModifierPanel *panels;
|
||||
|
||||
@@ -1791,6 +1791,12 @@ static PointerRNA rna_NodesModifierBake_node_get(PointerRNA *ptr)
|
||||
return RNA_pointer_create(const_cast<ID *>(&tree->id), &RNA_Node, const_cast<bNode *>(node));
|
||||
}
|
||||
|
||||
static StructRNA *rna_NodesModifierBake_data_block_typef(PointerRNA *ptr)
|
||||
{
|
||||
NodesModifierDataBlock *data_block = static_cast<NodesModifierDataBlock *>(ptr->data);
|
||||
return ID_code_to_RNA_type(data_block->id_type);
|
||||
}
|
||||
|
||||
bool rna_GreasePencilModifier_material_poll(PointerRNA *ptr, PointerRNA value)
|
||||
{
|
||||
Object *ob = reinterpret_cast<Object *>(ptr->owner_id);
|
||||
@@ -7269,8 +7275,63 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
|
||||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
static void rna_def_modifier_nodes_data_block(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "NodesModifierDataBlock", nullptr);
|
||||
RNA_def_struct_sdna(srna, "NodesModifierDataBlock");
|
||||
|
||||
RNA_define_lib_overridable(true);
|
||||
|
||||
prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Data-Block Name", "Name that is mapped to the referenced data-block");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "lib_name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Library Name",
|
||||
"Used when the data block is not local to the current .blend file but "
|
||||
"is linked from some library");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "ID");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
|
||||
RNA_def_property_pointer_funcs(
|
||||
prop, nullptr, nullptr, "rna_NodesModifierBake_data_block_typef", nullptr);
|
||||
RNA_def_property_ui_text(prop, "Data-Block", "");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
|
||||
|
||||
prop = RNA_def_property(srna, "id_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_id_type_items);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
|
||||
|
||||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
static void rna_def_modifier_nodes_bake_data_blocks(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "NodesModifierBakeDataBlocks", nullptr);
|
||||
RNA_def_struct_sdna(srna, "NodesModifierBake");
|
||||
RNA_def_struct_ui_text(
|
||||
srna, "Data-Blocks", "Collection of data-blocks that can be referenced by baked data");
|
||||
|
||||
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, nullptr, "active_data_block");
|
||||
}
|
||||
|
||||
static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
|
||||
{
|
||||
rna_def_modifier_nodes_bake_data_blocks(brna);
|
||||
|
||||
static EnumPropertyItem bake_mode_items[] = {
|
||||
{NODES_MODIFIER_BAKE_MODE_ANIMATION, "ANIMATION", 0, "Animation", "Bake a frame range"},
|
||||
{NODES_MODIFIER_BAKE_MODE_STILL, "STILL", 0, "Still", "Bake a single frame"},
|
||||
@@ -7330,6 +7391,11 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
|
||||
"none in some cases like missing linked data blocks");
|
||||
RNA_def_property_pointer_funcs(
|
||||
prop, "rna_NodesModifierBake_node_get", nullptr, nullptr, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "data_blocks", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "NodesModifierDataBlock");
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "data_blocks", "data_blocks_num");
|
||||
RNA_def_property_srna(prop, "NodesModifierBakeDataBlocks");
|
||||
}
|
||||
|
||||
static void rna_def_modifier_nodes_bakes(BlenderRNA *brna)
|
||||
@@ -7370,6 +7436,8 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
rna_def_modifier_nodes_data_block(brna);
|
||||
|
||||
rna_def_modifier_nodes_bake(brna);
|
||||
rna_def_modifier_nodes_bakes(brna);
|
||||
|
||||
@@ -7417,6 +7485,7 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
|
||||
rna_def_modifier_panel_open_prop(srna, "open_manage_panel", 1);
|
||||
rna_def_modifier_panel_open_prop(srna, "open_bake_panel", 2);
|
||||
rna_def_modifier_panel_open_prop(srna, "open_named_attributes_panel", 3);
|
||||
rna_def_modifier_panel_open_prop(srna, "open_bake_data_blocks_panel", 4);
|
||||
|
||||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ set(INC
|
||||
../render
|
||||
../windowmanager
|
||||
../../../intern/eigen
|
||||
../../../extern/fmtlib/include
|
||||
|
||||
# RNA_prototypes.h
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
@@ -122,6 +123,7 @@ set(LIB
|
||||
PRIVATE bf::depsgraph
|
||||
PUBLIC bf::dna
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
extern_fmtlib
|
||||
)
|
||||
|
||||
if(WITH_ALEMBIC)
|
||||
|
||||
@@ -37,4 +37,6 @@ struct NodesModifierRuntime {
|
||||
std::shared_ptr<bke::bake::ModifierCache> cache;
|
||||
};
|
||||
|
||||
void nodes_modifier_data_block_destruct(NodesModifierDataBlock *data_block, bool do_id_user);
|
||||
|
||||
} // namespace blender
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@@ -22,6 +23,7 @@
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_array_utils.hh"
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_curves_types.h"
|
||||
#include "DNA_defaults.h"
|
||||
@@ -39,6 +41,7 @@
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_bake_data_block_map.hh"
|
||||
#include "BKE_bake_geometry_nodes_modifier.hh"
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
@@ -65,6 +68,7 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
@@ -73,6 +77,7 @@
|
||||
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
#include "DEG_depsgraph_writeback_sync.hh"
|
||||
|
||||
#include "MOD_modifiertypes.hh"
|
||||
#include "MOD_nodes.hh"
|
||||
@@ -177,6 +182,14 @@ static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphCont
|
||||
}
|
||||
}
|
||||
|
||||
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
|
||||
for (const NodesModifierDataBlock &data_block : Span(bake.data_blocks, bake.data_blocks_num)) {
|
||||
if (data_block.id) {
|
||||
used_ids.add(data_block.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ID *id : used_ids) {
|
||||
switch ((ID_Type)GS(id->name)) {
|
||||
case ID_OB: {
|
||||
@@ -270,6 +283,13 @@ static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void
|
||||
settings->user_data, settings->ob, (ID **)&id_prop->data.pointer, IDWALK_CB_USER);
|
||||
},
|
||||
&settings);
|
||||
|
||||
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
|
||||
for (NodesModifierDataBlock &data_block : MutableSpan(bake.data_blocks, bake.data_blocks_num))
|
||||
{
|
||||
walk(user_data, ob, &data_block.id, IDWALK_CB_USER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void foreach_tex_link(ModifierData *md, Object *ob, TexWalkFunc walk, void *user_data)
|
||||
@@ -832,6 +852,53 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md)
|
||||
}
|
||||
}
|
||||
|
||||
class NodesModifierBakeDataBlockMap : public bake::BakeDataBlockMap {
|
||||
/** Protects access to `new_mappings` which may be added to from multiple threads. */
|
||||
std::mutex mutex_;
|
||||
|
||||
public:
|
||||
Map<bake::BakeDataBlockID, ID *> old_mappings;
|
||||
Map<bake::BakeDataBlockID, ID *> new_mappings;
|
||||
|
||||
ID *lookup_or_remember_missing(const bake::BakeDataBlockID &key) override
|
||||
{
|
||||
if (ID *id = this->old_mappings.lookup_default(key, nullptr)) {
|
||||
return id;
|
||||
}
|
||||
if (this->old_mappings.contains(key)) {
|
||||
/* Don't allow overwriting old mappings. */
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard lock{mutex_};
|
||||
return this->new_mappings.lookup_or_add(key, nullptr);
|
||||
}
|
||||
|
||||
void try_add(ID &id) override
|
||||
{
|
||||
bake::BakeDataBlockID key{id};
|
||||
if (this->old_mappings.contains(key)) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex_};
|
||||
this->new_mappings.add_overwrite(std::move(key), &id);
|
||||
}
|
||||
|
||||
private:
|
||||
ID *lookup_in_map(Map<bake::BakeDataBlockID, ID *> &map,
|
||||
const bake::BakeDataBlockID &key,
|
||||
const std::optional<ID_Type> &type)
|
||||
{
|
||||
ID *id = map.lookup_default(key, nullptr);
|
||||
if (!id) {
|
||||
return nullptr;
|
||||
}
|
||||
if (type && GS(id->name) != *type) {
|
||||
return nullptr;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
};
|
||||
|
||||
namespace sim_input = nodes::sim_input;
|
||||
namespace sim_output = nodes::sim_output;
|
||||
|
||||
@@ -920,7 +987,6 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
private:
|
||||
static constexpr float max_delta_frames = 1.0f;
|
||||
|
||||
mutable Map<int, std::unique_ptr<nodes::SimulationZoneBehavior>> behavior_by_zone_id_;
|
||||
const NodesModifierData &nmd_;
|
||||
const ModifierEvalContext &ctx_;
|
||||
const Main *bmain_;
|
||||
@@ -933,6 +999,13 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
bool has_invalid_simulation_ = false;
|
||||
|
||||
public:
|
||||
struct DataPerZone {
|
||||
nodes::SimulationZoneBehavior behavior;
|
||||
NodesModifierBakeDataBlockMap data_block_map;
|
||||
};
|
||||
|
||||
mutable Map<int, std::unique_ptr<DataPerZone>> data_by_zone_id;
|
||||
|
||||
NodesModifierSimulationParams(NodesModifierData &nmd, const ModifierEvalContext &ctx)
|
||||
: nmd_(nmd), ctx_(ctx)
|
||||
{
|
||||
@@ -1005,21 +1078,26 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard lock{modifier_cache_->mutex};
|
||||
return behavior_by_zone_id_
|
||||
.lookup_or_add_cb(zone_id,
|
||||
[&]() {
|
||||
auto info = std::make_unique<nodes::SimulationZoneBehavior>();
|
||||
this->init_simulation_info(zone_id, *info);
|
||||
return info;
|
||||
})
|
||||
.get();
|
||||
return &this->data_by_zone_id
|
||||
.lookup_or_add_cb(zone_id,
|
||||
[&]() {
|
||||
auto data = std::make_unique<DataPerZone>();
|
||||
data->behavior.data_block_map = &data->data_block_map;
|
||||
this->init_simulation_info(
|
||||
zone_id, data->behavior, data->data_block_map);
|
||||
return data;
|
||||
})
|
||||
->behavior;
|
||||
}
|
||||
|
||||
void init_simulation_info(const int zone_id, nodes::SimulationZoneBehavior &zone_behavior) const
|
||||
void init_simulation_info(const int zone_id,
|
||||
nodes::SimulationZoneBehavior &zone_behavior,
|
||||
NodesModifierBakeDataBlockMap &data_block_map) const
|
||||
{
|
||||
bake::SimulationNodeCache &node_cache =
|
||||
*modifier_cache_->simulation_cache_by_id.lookup_or_add_cb(
|
||||
zone_id, []() { return std::make_unique<bake::SimulationNodeCache>(); });
|
||||
const NodesModifierBake &bake = *nmd_.find_bake(zone_id);
|
||||
const IndexRange sim_frame_range = *bake::get_node_bake_frame_range(
|
||||
*scene_, *ctx_.object, nmd_, zone_id);
|
||||
const SubFrame sim_start_frame{int(sim_frame_range.first())};
|
||||
@@ -1037,6 +1115,14 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are no baked frames, we don't need keep track of the data-blocks. */
|
||||
if (!node_cache.bake.frames.is_empty()) {
|
||||
for (const NodesModifierDataBlock &data_block : Span{bake.data_blocks, bake.data_blocks_num})
|
||||
{
|
||||
data_block_map.old_mappings.add(data_block, data_block.id);
|
||||
}
|
||||
}
|
||||
|
||||
const BakeFrameIndices frame_indices = get_bake_frame_indices(node_cache.bake.frames,
|
||||
current_frame_);
|
||||
if (node_cache.cache_status == bake::CacheStatus::Baked) {
|
||||
@@ -1230,7 +1316,6 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
|
||||
class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
private:
|
||||
mutable Map<int, std::unique_ptr<nodes::BakeNodeBehavior>> behavior_by_node_id_;
|
||||
const NodesModifierData &nmd_;
|
||||
const ModifierEvalContext &ctx_;
|
||||
Main *bmain_;
|
||||
@@ -1239,6 +1324,13 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
bool depsgraph_is_active_;
|
||||
|
||||
public:
|
||||
struct DataPerNode {
|
||||
nodes::BakeNodeBehavior behavior;
|
||||
NodesModifierBakeDataBlockMap data_block_map;
|
||||
};
|
||||
|
||||
mutable Map<int, std::unique_ptr<DataPerNode>> data_by_node_id;
|
||||
|
||||
NodesModifierBakeParams(NodesModifierData &nmd, const ModifierEvalContext &ctx)
|
||||
: nmd_(nmd), ctx_(ctx)
|
||||
{
|
||||
@@ -1255,27 +1347,36 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard lock{modifier_cache_->mutex};
|
||||
return behavior_by_node_id_
|
||||
.lookup_or_add_cb(id,
|
||||
[&]() {
|
||||
auto info = std::make_unique<nodes::BakeNodeBehavior>();
|
||||
this->init_bake_behavior(id, *info);
|
||||
return info;
|
||||
})
|
||||
.get();
|
||||
return &this->data_by_node_id
|
||||
.lookup_or_add_cb(id,
|
||||
[&]() {
|
||||
auto data = std::make_unique<DataPerNode>();
|
||||
data->behavior.data_block_map = &data->data_block_map;
|
||||
this->init_bake_behavior(
|
||||
id, data->behavior, data->data_block_map);
|
||||
return data;
|
||||
})
|
||||
->behavior;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void init_bake_behavior(const int id, nodes::BakeNodeBehavior &behavior) const
|
||||
void init_bake_behavior(const int id,
|
||||
nodes::BakeNodeBehavior &behavior,
|
||||
NodesModifierBakeDataBlockMap &data_block_map) const
|
||||
{
|
||||
bake::BakeNodeCache &node_cache = *modifier_cache_->bake_cache_by_id.lookup_or_add_cb(
|
||||
id, []() { return std::make_unique<bake::BakeNodeCache>(); });
|
||||
const NodesModifierBake &bake = *nmd_.find_bake(id);
|
||||
|
||||
for (const NodesModifierDataBlock &data_block : Span{bake.data_blocks, bake.data_blocks_num}) {
|
||||
data_block_map.old_mappings.add(data_block, data_block.id);
|
||||
}
|
||||
|
||||
if (depsgraph_is_active_) {
|
||||
if (modifier_cache_->requested_bakes.contains(id)) {
|
||||
/* This node is baked during the current evaluation. */
|
||||
auto &store_info = behavior.emplace<sim_output::StoreNewState>();
|
||||
auto &store_info = behavior.behavior.emplace<sim_output::StoreNewState>();
|
||||
store_info.store_fn = [modifier_cache = modifier_cache_,
|
||||
node_cache = &node_cache,
|
||||
current_frame = current_frame_](bake::BakeState state) {
|
||||
@@ -1304,7 +1405,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
}
|
||||
|
||||
if (node_cache.bake.frames.is_empty()) {
|
||||
behavior.emplace<sim_output::PassThrough>();
|
||||
behavior.behavior.emplace<sim_output::PassThrough>();
|
||||
return;
|
||||
}
|
||||
const BakeFrameIndices frame_indices = get_bake_frame_indices(node_cache.bake.frames,
|
||||
@@ -1337,7 +1438,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
if (this->check_read_error(frame_cache, behavior)) {
|
||||
return;
|
||||
}
|
||||
auto &read_single_info = behavior.emplace<sim_output::ReadSingle>();
|
||||
auto &read_single_info = behavior.behavior.emplace<sim_output::ReadSingle>();
|
||||
read_single_info.state = frame_cache.state;
|
||||
}
|
||||
|
||||
@@ -1355,7 +1456,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto &read_interpolated_info = behavior.emplace<sim_output::ReadInterpolated>();
|
||||
auto &read_interpolated_info = behavior.behavior.emplace<sim_output::ReadInterpolated>();
|
||||
read_interpolated_info.mix_factor = (float(current_frame_) - float(prev_frame_cache.frame)) /
|
||||
(float(next_frame_cache.frame) -
|
||||
float(prev_frame_cache.frame));
|
||||
@@ -1367,7 +1468,7 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
nodes::BakeNodeBehavior &behavior) const
|
||||
{
|
||||
if (frame_cache.meta_path && frame_cache.state.items_by_id.is_empty()) {
|
||||
auto &read_error_info = behavior.emplace<sim_output::ReadError>();
|
||||
auto &read_error_info = behavior.behavior.emplace<sim_output::ReadError>();
|
||||
read_error_info.message = RPT_("Can not load the baked data");
|
||||
return true;
|
||||
}
|
||||
@@ -1375,6 +1476,169 @@ class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
}
|
||||
};
|
||||
|
||||
static void add_missing_data_block_mappings(
|
||||
NodesModifierBake &bake,
|
||||
const Span<bake::BakeDataBlockID> missing,
|
||||
FunctionRef<ID *(const bake::BakeDataBlockID &)> get_data_block)
|
||||
{
|
||||
const int old_num = bake.data_blocks_num;
|
||||
const int new_num = old_num + missing.size();
|
||||
bake.data_blocks = reinterpret_cast<NodesModifierDataBlock *>(
|
||||
MEM_recallocN(bake.data_blocks, sizeof(NodesModifierDataBlock) * new_num));
|
||||
for (const int i : missing.index_range()) {
|
||||
NodesModifierDataBlock &data_block = bake.data_blocks[old_num + i];
|
||||
const blender::bke::bake::BakeDataBlockID &key = missing[i];
|
||||
|
||||
data_block.id_name = BLI_strdup(key.id_name.c_str());
|
||||
if (!key.lib_name.empty()) {
|
||||
data_block.lib_name = BLI_strdup(key.lib_name.c_str());
|
||||
}
|
||||
data_block.id_type = int(key.type);
|
||||
ID *id = get_data_block(key);
|
||||
if (id) {
|
||||
data_block.id = id;
|
||||
}
|
||||
}
|
||||
bake.data_blocks_num = new_num;
|
||||
}
|
||||
|
||||
void nodes_modifier_data_block_destruct(NodesModifierDataBlock *data_block, const bool do_id_user)
|
||||
{
|
||||
MEM_SAFE_FREE(data_block->id_name);
|
||||
MEM_SAFE_FREE(data_block->lib_name);
|
||||
if (do_id_user) {
|
||||
id_us_min(data_block->id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* During evaluation we might have baked geometry that contains references to other data-blocks
|
||||
* (such as materials). We need to make sure that those data-blocks stay dependencies of the
|
||||
* modifier. Otherwise, the data-block references might not work when the baked data is loaded
|
||||
* again. Therefor, the dependencies are written back to the original modifier.
|
||||
*/
|
||||
static void add_data_block_items_writeback(const ModifierEvalContext &ctx,
|
||||
NodesModifierData &nmd_eval,
|
||||
NodesModifierData &nmd_orig,
|
||||
NodesModifierSimulationParams &simulation_params,
|
||||
NodesModifierBakeParams &bake_params)
|
||||
{
|
||||
Depsgraph *depsgraph = ctx.depsgraph;
|
||||
Main *bmain = DEG_get_bmain(depsgraph);
|
||||
|
||||
struct DataPerBake {
|
||||
bool reset_first = false;
|
||||
Map<bake::BakeDataBlockID, ID *> new_mappings;
|
||||
};
|
||||
Map<int, DataPerBake> writeback_data;
|
||||
for (auto item : simulation_params.data_by_zone_id.items()) {
|
||||
DataPerBake data;
|
||||
NodesModifierBake &bake = *nmd_eval.find_bake(item.key);
|
||||
if (item.value->data_block_map.old_mappings.size() < bake.data_blocks_num) {
|
||||
data.reset_first = true;
|
||||
}
|
||||
if (bake::SimulationNodeCache *node_cache = nmd_eval.runtime->cache->get_simulation_node_cache(
|
||||
item.key))
|
||||
{
|
||||
/* Only writeback if the bake node has actually baked anything. */
|
||||
if (!node_cache->bake.frames.is_empty()) {
|
||||
data.new_mappings = std::move(item.value->data_block_map.new_mappings);
|
||||
}
|
||||
}
|
||||
if (data.reset_first || !data.new_mappings.is_empty()) {
|
||||
writeback_data.add(item.key, std::move(data));
|
||||
}
|
||||
}
|
||||
for (auto item : bake_params.data_by_node_id.items()) {
|
||||
if (bake::BakeNodeCache *node_cache = nmd_eval.runtime->cache->get_bake_node_cache(item.key)) {
|
||||
/* Only writeback if the bake node has actually baked anything. */
|
||||
if (!node_cache->bake.frames.is_empty()) {
|
||||
DataPerBake data;
|
||||
data.new_mappings = std::move(item.value->data_block_map.new_mappings);
|
||||
writeback_data.add(item.key, std::move(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (writeback_data.is_empty()) {
|
||||
/* Nothing to do. */
|
||||
return;
|
||||
}
|
||||
|
||||
deg::sync_writeback::add(
|
||||
*depsgraph,
|
||||
[depsgraph = depsgraph,
|
||||
object_eval = ctx.object,
|
||||
bmain,
|
||||
&nmd_orig,
|
||||
&nmd_eval,
|
||||
writeback_data = std::move(writeback_data)]() {
|
||||
for (auto item : writeback_data.items()) {
|
||||
const int bake_id = item.key;
|
||||
DataPerBake data = item.value;
|
||||
|
||||
NodesModifierBake &bake_orig = *nmd_orig.find_bake(bake_id);
|
||||
NodesModifierBake &bake_eval = *nmd_eval.find_bake(bake_id);
|
||||
|
||||
if (data.reset_first) {
|
||||
/* Reset data-block list on original data. */
|
||||
dna::array::clear<NodesModifierDataBlock>(&bake_orig.data_blocks,
|
||||
&bake_orig.data_blocks_num,
|
||||
&bake_orig.active_data_block,
|
||||
[](NodesModifierDataBlock *data_block) {
|
||||
nodes_modifier_data_block_destruct(
|
||||
data_block, true);
|
||||
});
|
||||
/* Reset data-block list on evaluated data. */
|
||||
dna::array::clear<NodesModifierDataBlock>(&bake_eval.data_blocks,
|
||||
&bake_eval.data_blocks_num,
|
||||
&bake_eval.active_data_block,
|
||||
[](NodesModifierDataBlock *data_block) {
|
||||
nodes_modifier_data_block_destruct(
|
||||
data_block, false);
|
||||
});
|
||||
}
|
||||
|
||||
Vector<bake::BakeDataBlockID> sorted_new_mappings;
|
||||
sorted_new_mappings.extend(data.new_mappings.keys().begin(),
|
||||
data.new_mappings.keys().end());
|
||||
bool needs_reevaluation = false;
|
||||
/* Add new data block mappings to the original modifier. This may do a name lookup in
|
||||
* bmain to find the data block if there is not faster way to get it. */
|
||||
add_missing_data_block_mappings(
|
||||
bake_orig, sorted_new_mappings, [&](const bake::BakeDataBlockID &key) -> ID * {
|
||||
ID *id_orig = nullptr;
|
||||
if (ID *id_eval = data.new_mappings.lookup_default(key, nullptr)) {
|
||||
id_orig = DEG_get_original_id(id_eval);
|
||||
}
|
||||
else {
|
||||
needs_reevaluation = true;
|
||||
id_orig = BKE_libblock_find_name_and_library(
|
||||
bmain, short(key.type), key.id_name.c_str(), key.lib_name.c_str());
|
||||
}
|
||||
if (id_orig) {
|
||||
id_us_plus(id_orig);
|
||||
}
|
||||
return id_orig;
|
||||
});
|
||||
/* Add new data block mappings to the evaluated modifier. In most cases this makes it so
|
||||
* the evaluated modifier is in the same state as if it were copied from the updated
|
||||
* original again. The exception is when a missing data block was found that is not in
|
||||
* the depsgraph currently. */
|
||||
add_missing_data_block_mappings(
|
||||
bake_eval, sorted_new_mappings, [&](const bake::BakeDataBlockID &key) -> ID * {
|
||||
return data.new_mappings.lookup_default(key, nullptr);
|
||||
});
|
||||
|
||||
if (needs_reevaluation) {
|
||||
Object *object_orig = DEG_get_original_object(object_eval);
|
||||
DEG_id_tag_update(&object_orig->id, ID_RECALC_GEOMETRY);
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void modifyGeometry(ModifierData *md,
|
||||
const ModifierEvalContext *ctx,
|
||||
bke::GeometrySet &geometry_set)
|
||||
@@ -1466,6 +1730,10 @@ static void modifyGeometry(ModifierData *md,
|
||||
nmd_orig->runtime->eval_log = std::move(eval_log);
|
||||
}
|
||||
|
||||
if (DEG_is_active(ctx->depsgraph)) {
|
||||
add_data_block_items_writeback(*ctx, *nmd, *nmd_orig, simulation_params, bake_params);
|
||||
}
|
||||
|
||||
if (use_orig_index_verts || use_orig_index_edges || use_orig_index_faces) {
|
||||
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
|
||||
/* Add #CD_ORIGINDEX layers if they don't exist already. This is required because the
|
||||
@@ -2108,6 +2376,13 @@ static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const Modi
|
||||
BLO_write_struct_array(writer, NodesModifierBake, nmd->bakes_num, nmd->bakes);
|
||||
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
|
||||
BLO_write_string(writer, bake.directory);
|
||||
|
||||
BLO_write_struct_array(
|
||||
writer, NodesModifierDataBlock, bake.data_blocks_num, bake.data_blocks);
|
||||
for (const NodesModifierDataBlock &item : Span(bake.data_blocks, bake.data_blocks_num)) {
|
||||
BLO_write_string(writer, item.id_name);
|
||||
BLO_write_string(writer, item.lib_name);
|
||||
}
|
||||
}
|
||||
BLO_write_struct_array(writer, NodesModifierPanel, nmd->panels_num, nmd->panels);
|
||||
|
||||
@@ -2141,6 +2416,13 @@ static void blend_read(BlendDataReader *reader, ModifierData *md)
|
||||
BLO_read_data_address(reader, &nmd->bakes);
|
||||
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
|
||||
BLO_read_data_address(reader, &bake.directory);
|
||||
|
||||
BLO_read_data_address(reader, &bake.data_blocks);
|
||||
for (NodesModifierDataBlock &data_block : MutableSpan(bake.data_blocks, bake.data_blocks_num))
|
||||
{
|
||||
BLO_read_data_address(reader, &data_block.id_name);
|
||||
BLO_read_data_address(reader, &data_block.lib_name);
|
||||
}
|
||||
}
|
||||
BLO_read_data_address(reader, &nmd->panels);
|
||||
|
||||
@@ -2162,6 +2444,18 @@ static void copy_data(const ModifierData *md, ModifierData *target, const int fl
|
||||
if (bake.directory) {
|
||||
bake.directory = BLI_strdup(bake.directory);
|
||||
}
|
||||
if (bake.data_blocks) {
|
||||
bake.data_blocks = static_cast<NodesModifierDataBlock *>(MEM_dupallocN(bake.data_blocks));
|
||||
for (const int i : IndexRange(bake.data_blocks_num)) {
|
||||
NodesModifierDataBlock &data_block = bake.data_blocks[i];
|
||||
if (data_block.id_name) {
|
||||
data_block.id_name = BLI_strdup(data_block.id_name);
|
||||
}
|
||||
if (data_block.lib_name) {
|
||||
data_block.lib_name = BLI_strdup(data_block.lib_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2198,6 +2492,13 @@ static void free_data(ModifierData *md)
|
||||
|
||||
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
|
||||
MEM_SAFE_FREE(bake.directory);
|
||||
|
||||
for (NodesModifierDataBlock &data_block : MutableSpan(bake.data_blocks, bake.data_blocks_num))
|
||||
{
|
||||
MEM_SAFE_FREE(data_block.id_name);
|
||||
MEM_SAFE_FREE(data_block.lib_name);
|
||||
}
|
||||
MEM_SAFE_FREE(bake.data_blocks);
|
||||
}
|
||||
MEM_SAFE_FREE(nmd->bakes);
|
||||
|
||||
|
||||
@@ -121,6 +121,7 @@ using Behavior = std::variant<PassThrough, StoreNewState, ReadSingle, ReadInterp
|
||||
struct SimulationZoneBehavior {
|
||||
sim_input::Behavior input;
|
||||
sim_output::Behavior output;
|
||||
bke::bake::BakeDataBlockMap *data_block_map = nullptr;
|
||||
};
|
||||
|
||||
class GeoNodesSimulationParams {
|
||||
@@ -133,8 +134,11 @@ class GeoNodesSimulationParams {
|
||||
virtual SimulationZoneBehavior *get(const int zone_id) const = 0;
|
||||
};
|
||||
|
||||
/** The set of possible behaviors are the same for both of these nodes currently. */
|
||||
using BakeNodeBehavior = sim_output::Behavior;
|
||||
struct BakeNodeBehavior {
|
||||
/** The set of possible behaviors are the same for both of these nodes currently. */
|
||||
sim_output::Behavior behavior;
|
||||
bke::bake::BakeDataBlockMap *data_block_map = nullptr;
|
||||
};
|
||||
|
||||
class GeoNodesBakeParams {
|
||||
public:
|
||||
|
||||
@@ -99,18 +99,22 @@ const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type);
|
||||
const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item);
|
||||
|
||||
bke::bake::BakeState move_values_to_simulation_state(
|
||||
Span<NodeSimulationItem> node_simulation_items, Span<void *> input_values);
|
||||
Span<NodeSimulationItem> node_simulation_items,
|
||||
Span<void *> input_values,
|
||||
bke::bake::BakeDataBlockMap *data_block_map);
|
||||
void move_simulation_state_to_values(Span<NodeSimulationItem> node_simulation_items,
|
||||
bke::bake::BakeState zone_state,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &sim_output_node,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
Span<void *> r_output_values);
|
||||
void copy_simulation_state_to_values(Span<NodeSimulationItem> node_simulation_items,
|
||||
const bke::bake::BakeStateRef &zone_state,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &sim_output_node,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
Span<void *> r_output_values);
|
||||
|
||||
void copy_with_checked_indices(const GVArray &src,
|
||||
@@ -155,4 +159,6 @@ const EnumPropertyItem *grid_socket_type_items_filter_fn(bContext *C,
|
||||
|
||||
void node_geo_exec_with_missing_openvdb(GeoNodeExecParams ¶ms);
|
||||
|
||||
void draw_data_blocks(const bContext *C, uiLayout *layout, PointerRNA &bake_rna);
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "BKE_bake_geometry_nodes_modifier.hh"
|
||||
#include "BKE_bake_items_socket.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "ED_node.hh"
|
||||
|
||||
@@ -191,24 +192,25 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
this->set_default_outputs(params);
|
||||
return;
|
||||
}
|
||||
if (auto *info = std::get_if<sim_output::ReadSingle>(behavior)) {
|
||||
this->output_cached_state(params, user_data, info->state);
|
||||
if (auto *info = std::get_if<sim_output::ReadSingle>(&behavior->behavior)) {
|
||||
this->output_cached_state(params, user_data, behavior->data_block_map, info->state);
|
||||
}
|
||||
else if (auto *info = std::get_if<sim_output::ReadInterpolated>(behavior)) {
|
||||
else if (auto *info = std::get_if<sim_output::ReadInterpolated>(&behavior->behavior)) {
|
||||
this->output_mixed_cached_state(params,
|
||||
behavior->data_block_map,
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
info->prev_state,
|
||||
info->next_state,
|
||||
info->mix_factor);
|
||||
}
|
||||
else if (std::get_if<sim_output::PassThrough>(behavior)) {
|
||||
this->pass_through(params, user_data);
|
||||
else if (std::get_if<sim_output::PassThrough>(&behavior->behavior)) {
|
||||
this->pass_through(params, user_data, behavior->data_block_map);
|
||||
}
|
||||
else if (auto *info = std::get_if<sim_output::StoreNewState>(behavior)) {
|
||||
this->store(params, user_data, *info);
|
||||
else if (auto *info = std::get_if<sim_output::StoreNewState>(&behavior->behavior)) {
|
||||
this->store(params, user_data, behavior->data_block_map, *info);
|
||||
}
|
||||
else if (auto *info = std::get_if<sim_output::ReadError>(behavior)) {
|
||||
else if (auto *info = std::get_if<sim_output::ReadError>(&behavior->behavior)) {
|
||||
if (geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(
|
||||
user_data))
|
||||
{
|
||||
@@ -227,9 +229,12 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
set_default_remaining_node_outputs(params, node_);
|
||||
}
|
||||
|
||||
void pass_through(lf::Params ¶ms, GeoNodesLFUserData &user_data) const
|
||||
void pass_through(lf::Params ¶ms,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map) const
|
||||
{
|
||||
std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(params);
|
||||
std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(params,
|
||||
data_block_map);
|
||||
if (!bake_state) {
|
||||
/* Wait for inputs to be computed. */
|
||||
return;
|
||||
@@ -239,6 +244,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
output_values[i] = params.get_output_data_ptr(i);
|
||||
}
|
||||
this->move_bake_state_to_values(std::move(*bake_state),
|
||||
data_block_map,
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
output_values);
|
||||
@@ -249,19 +255,22 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
|
||||
void store(lf::Params ¶ms,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const sim_output::StoreNewState &info) const
|
||||
{
|
||||
std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(params);
|
||||
std::optional<bake::BakeState> bake_state = this->get_bake_state_from_inputs(params,
|
||||
data_block_map);
|
||||
if (!bake_state) {
|
||||
/* Wait for inputs to be computed. */
|
||||
return;
|
||||
}
|
||||
this->output_cached_state(params, user_data, *bake_state);
|
||||
this->output_cached_state(params, user_data, data_block_map, *bake_state);
|
||||
info.store_fn(std::move(*bake_state));
|
||||
}
|
||||
|
||||
void output_cached_state(lf::Params ¶ms,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const bake::BakeStateRef &bake_state) const
|
||||
{
|
||||
Array<void *> output_values(bake_items_.size());
|
||||
@@ -269,6 +278,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
output_values[i] = params.get_output_data_ptr(i);
|
||||
}
|
||||
this->copy_bake_state_to_values(bake_state,
|
||||
data_block_map,
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
output_values);
|
||||
@@ -278,6 +288,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
}
|
||||
|
||||
void output_mixed_cached_state(lf::Params ¶ms,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bake::BakeStateRef &prev_state,
|
||||
@@ -288,7 +299,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
for (const int i : bake_items_.index_range()) {
|
||||
output_values[i] = params.get_output_data_ptr(i);
|
||||
}
|
||||
this->copy_bake_state_to_values(prev_state, self_object, compute_context, output_values);
|
||||
this->copy_bake_state_to_values(
|
||||
prev_state, data_block_map, self_object, compute_context, output_values);
|
||||
|
||||
Array<void *> next_values(bake_items_.size());
|
||||
LinearAllocator<> allocator;
|
||||
@@ -296,7 +308,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
const CPPType &type = *outputs_[i].type;
|
||||
next_values[i] = allocator.allocate(type.size(), type.alignment());
|
||||
}
|
||||
this->copy_bake_state_to_values(next_state, self_object, compute_context, next_values);
|
||||
this->copy_bake_state_to_values(
|
||||
next_state, data_block_map, self_object, compute_context, next_values);
|
||||
|
||||
for (const int i : bake_items_.index_range()) {
|
||||
mix_baked_data_item(eNodeSocketDatatype(bake_items_[i].socket_type),
|
||||
@@ -315,7 +328,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<bake::BakeState> get_bake_state_from_inputs(lf::Params ¶ms) const
|
||||
std::optional<bake::BakeState> get_bake_state_from_inputs(
|
||||
lf::Params ¶ms, bke::bake::BakeDataBlockMap *data_block_map) const
|
||||
{
|
||||
Array<void *> input_values(bake_items_.size());
|
||||
for (const int i : bake_items_.index_range()) {
|
||||
@@ -327,7 +341,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
}
|
||||
|
||||
Array<std::unique_ptr<bake::BakeItem>> bake_items = bake::move_socket_values_to_bake_items(
|
||||
input_values, bake_socket_config_);
|
||||
input_values, bake_socket_config_, data_block_map);
|
||||
|
||||
bake::BakeState bake_state;
|
||||
for (const int i : bake_items_.index_range()) {
|
||||
@@ -341,6 +355,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
}
|
||||
|
||||
void move_bake_state_to_values(bake::BakeState bake_state,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
Span<void *> r_output_values) const
|
||||
@@ -354,6 +369,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
bake::move_bake_items_to_socket_values(
|
||||
bake_items,
|
||||
bake_socket_config_,
|
||||
data_block_map,
|
||||
[&](const int i, const CPPType &type) {
|
||||
return this->make_attribute_field(self_object, compute_context, bake_items_[i], type);
|
||||
},
|
||||
@@ -361,6 +377,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
}
|
||||
|
||||
void copy_bake_state_to_values(const bake::BakeStateRef &bake_state,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
Span<void *> r_output_values) const
|
||||
@@ -373,6 +390,7 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
bake::copy_bake_items_to_socket_values(
|
||||
bake_items,
|
||||
bake_socket_config_,
|
||||
data_block_map,
|
||||
[&](const int i, const CPPType &type) {
|
||||
return this->make_attribute_field(self_object, compute_context, bake_items_[i], type);
|
||||
},
|
||||
@@ -591,6 +609,8 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
|
||||
uiItemR(subcol, &ctx.bake_rna, "frame_end", UI_ITEM_NONE, "End", ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
draw_data_blocks(C, layout, ctx.bake_rna);
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
@@ -613,6 +633,64 @@ NOD_REGISTER_NODE(node_register)
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void draw_bake_data_block_list_item(uiList * /*ui_list*/,
|
||||
const bContext * /*C*/,
|
||||
uiLayout *layout,
|
||||
PointerRNA * /*idataptr*/,
|
||||
PointerRNA *itemptr,
|
||||
int /*icon*/,
|
||||
PointerRNA * /*active_dataptr*/,
|
||||
const char * /*active_propname*/,
|
||||
int /*index*/,
|
||||
int /*flt_flag*/)
|
||||
{
|
||||
auto &data_block = *static_cast<NodesModifierDataBlock *>(itemptr->data);
|
||||
uiLayout *row = uiLayoutRow(layout, true);
|
||||
|
||||
std::string name;
|
||||
if (StringRef(data_block.lib_name).is_empty()) {
|
||||
name = data_block.id_name;
|
||||
}
|
||||
else {
|
||||
name = fmt::format("{} [{}]", data_block.id_name, data_block.lib_name);
|
||||
}
|
||||
|
||||
uiItemR(row, itemptr, "id", UI_ITEM_NONE, name.c_str(), ICON_NONE);
|
||||
}
|
||||
|
||||
void draw_data_blocks(const bContext *C, uiLayout *layout, PointerRNA &bake_rna)
|
||||
{
|
||||
static const uiListType *data_block_list = []() {
|
||||
uiListType *list = MEM_cnew<uiListType>(__func__);
|
||||
STRNCPY(list->idname, "DATA_UL_nodes_modifier_data_blocks");
|
||||
list->draw_item = draw_bake_data_block_list_item;
|
||||
WM_uilisttype_add(list);
|
||||
return list;
|
||||
}();
|
||||
|
||||
PointerRNA data_blocks_ptr = RNA_pointer_create(
|
||||
bake_rna.owner_id, &RNA_NodesModifierBakeDataBlocks, bake_rna.data);
|
||||
|
||||
if (uiLayout *panel = uiLayoutPanel(
|
||||
C, layout, "data_block_references", true, TIP_("Data-Block References")))
|
||||
{
|
||||
uiTemplateList(panel,
|
||||
C,
|
||||
data_block_list->idname,
|
||||
"",
|
||||
&bake_rna,
|
||||
"data_blocks",
|
||||
&data_blocks_ptr,
|
||||
"active_index",
|
||||
nullptr,
|
||||
3,
|
||||
5,
|
||||
UILST_LAYOUT_DEFAULT,
|
||||
0,
|
||||
UI_TEMPLATE_LIST_FLAG_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<LazyFunction> get_bake_lazy_function(
|
||||
const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
|
||||
{
|
||||
|
||||
@@ -89,15 +89,17 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
|
||||
float delta_time = 0.0f;
|
||||
if (auto *info = std::get_if<sim_input::OutputCopy>(&input_behavior)) {
|
||||
delta_time = info->delta_time;
|
||||
this->output_simulation_state_copy(params, user_data, info->state);
|
||||
this->output_simulation_state_copy(
|
||||
params, user_data, zone_behavior->data_block_map, info->state);
|
||||
}
|
||||
else if (auto *info = std::get_if<sim_input::OutputMove>(&input_behavior)) {
|
||||
delta_time = info->delta_time;
|
||||
this->output_simulation_state_move(params, user_data, std::move(info->state));
|
||||
this->output_simulation_state_move(
|
||||
params, user_data, zone_behavior->data_block_map, std::move(info->state));
|
||||
}
|
||||
else if (std::get_if<sim_input::PassThrough>(&input_behavior)) {
|
||||
delta_time = 0.0f;
|
||||
this->pass_through(params, user_data);
|
||||
this->pass_through(params, user_data, zone_behavior->data_block_map);
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
@@ -114,6 +116,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
|
||||
|
||||
void output_simulation_state_copy(lf::Params ¶ms,
|
||||
const GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const bke::bake::BakeStateRef &zone_state) const
|
||||
{
|
||||
Array<void *> outputs(simulation_items_.size());
|
||||
@@ -125,6 +128,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
node_,
|
||||
data_block_map,
|
||||
outputs);
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
params.output_set(i + 1);
|
||||
@@ -133,6 +137,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
|
||||
|
||||
void output_simulation_state_move(lf::Params ¶ms,
|
||||
const GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
bke::bake::BakeState zone_state) const
|
||||
{
|
||||
Array<void *> outputs(simulation_items_.size());
|
||||
@@ -144,13 +149,16 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
node_,
|
||||
data_block_map,
|
||||
outputs);
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
params.output_set(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void pass_through(lf::Params ¶ms, const GeoNodesLFUserData &user_data) const
|
||||
void pass_through(lf::Params ¶ms,
|
||||
const GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map) const
|
||||
{
|
||||
Array<void *> input_values(inputs_.size());
|
||||
for (const int i : inputs_.index_range()) {
|
||||
@@ -163,9 +171,9 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
|
||||
/* Instead of outputting the initial values directly, convert them to a simulation state and
|
||||
* then back. This ensures that some geometry processing happens on the data consistently (e.g.
|
||||
* removing anonymous attributes). */
|
||||
bke::bake::BakeState bake_state = move_values_to_simulation_state(simulation_items_,
|
||||
input_values);
|
||||
this->output_simulation_state_move(params, user_data, std::move(bake_state));
|
||||
bke::bake::BakeState bake_state = move_values_to_simulation_state(
|
||||
simulation_items_, input_values, data_block_map);
|
||||
this->output_simulation_state_move(params, user_data, data_block_map, std::move(bake_state));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ void move_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &node,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
Span<void *> r_output_values)
|
||||
{
|
||||
const bke::bake::BakeSocketConfig config = make_bake_socket_config(node_simulation_items);
|
||||
@@ -117,6 +118,7 @@ void move_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
|
||||
bke::bake::move_bake_items_to_socket_values(
|
||||
bake_items,
|
||||
config,
|
||||
data_block_map,
|
||||
[&](const int i, const CPPType &type) {
|
||||
return make_attribute_field(
|
||||
self_object, compute_context, node, node_simulation_items[i], type);
|
||||
@@ -129,6 +131,7 @@ void copy_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &node,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
Span<void *> r_output_values)
|
||||
{
|
||||
const bke::bake::BakeSocketConfig config = make_bake_socket_config(node_simulation_items);
|
||||
@@ -142,6 +145,7 @@ void copy_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
|
||||
bke::bake::copy_bake_items_to_socket_values(
|
||||
bake_items,
|
||||
config,
|
||||
data_block_map,
|
||||
[&](const int i, const CPPType &type) {
|
||||
return make_attribute_field(
|
||||
self_object, compute_context, node, node_simulation_items[i], type);
|
||||
@@ -150,12 +154,14 @@ void copy_simulation_state_to_values(const Span<NodeSimulationItem> node_simulat
|
||||
}
|
||||
|
||||
bke::bake::BakeState move_values_to_simulation_state(
|
||||
const Span<NodeSimulationItem> node_simulation_items, const Span<void *> input_values)
|
||||
const Span<NodeSimulationItem> node_simulation_items,
|
||||
const Span<void *> input_values,
|
||||
bke::bake::BakeDataBlockMap *data_block_map)
|
||||
{
|
||||
const bke::bake::BakeSocketConfig config = make_bake_socket_config(node_simulation_items);
|
||||
|
||||
Array<std::unique_ptr<bke::bake::BakeItem>> bake_items =
|
||||
bke::bake::move_socket_values_to_bake_items(input_values, config);
|
||||
bke::bake::move_socket_values_to_bake_items(input_values, config, data_block_map);
|
||||
|
||||
bke::bake::BakeState bake_state;
|
||||
for (const int i : node_simulation_items.index_range()) {
|
||||
@@ -525,10 +531,11 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
}
|
||||
sim_output::Behavior &output_behavior = zone_behavior->output;
|
||||
if (auto *info = std::get_if<sim_output::ReadSingle>(&output_behavior)) {
|
||||
this->output_cached_state(params, user_data, info->state);
|
||||
this->output_cached_state(params, user_data, zone_behavior->data_block_map, info->state);
|
||||
}
|
||||
else if (auto *info = std::get_if<sim_output::ReadInterpolated>(&output_behavior)) {
|
||||
this->output_mixed_cached_state(params,
|
||||
zone_behavior->data_block_map,
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
info->prev_state,
|
||||
@@ -536,10 +543,10 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
info->mix_factor);
|
||||
}
|
||||
else if (std::get_if<sim_output::PassThrough>(&output_behavior)) {
|
||||
this->pass_through(params, user_data);
|
||||
this->pass_through(params, user_data, zone_behavior->data_block_map);
|
||||
}
|
||||
else if (auto *info = std::get_if<sim_output::StoreNewState>(&output_behavior)) {
|
||||
this->store_new_state(params, user_data, *info);
|
||||
this->store_new_state(params, user_data, zone_behavior->data_block_map, *info);
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
@@ -553,6 +560,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
|
||||
void output_cached_state(lf::Params ¶ms,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const bke::bake::BakeStateRef &state) const
|
||||
{
|
||||
Array<void *> output_values(simulation_items_.size());
|
||||
@@ -564,6 +572,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
node_,
|
||||
data_block_map,
|
||||
output_values);
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
params.output_set(i);
|
||||
@@ -571,6 +580,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
}
|
||||
|
||||
void output_mixed_cached_state(lf::Params ¶ms,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bke::bake::BakeStateRef &prev_state,
|
||||
@@ -581,8 +591,13 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
output_values[i] = params.get_output_data_ptr(i);
|
||||
}
|
||||
copy_simulation_state_to_values(
|
||||
simulation_items_, prev_state, self_object, compute_context, node_, output_values);
|
||||
copy_simulation_state_to_values(simulation_items_,
|
||||
prev_state,
|
||||
self_object,
|
||||
compute_context,
|
||||
node_,
|
||||
data_block_map,
|
||||
output_values);
|
||||
|
||||
Array<void *> next_values(simulation_items_.size());
|
||||
LinearAllocator<> allocator;
|
||||
@@ -590,8 +605,13 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
const CPPType &type = *outputs_[i].type;
|
||||
next_values[i] = allocator.allocate(type.size(), type.alignment());
|
||||
}
|
||||
copy_simulation_state_to_values(
|
||||
simulation_items_, next_state, self_object, compute_context, node_, next_values);
|
||||
copy_simulation_state_to_values(simulation_items_,
|
||||
next_state,
|
||||
self_object,
|
||||
compute_context,
|
||||
node_,
|
||||
data_block_map,
|
||||
next_values);
|
||||
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
mix_baked_data_item(eNodeSocketDatatype(simulation_items_[i].socket_type),
|
||||
@@ -610,10 +630,12 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
}
|
||||
}
|
||||
|
||||
void pass_through(lf::Params ¶ms, GeoNodesLFUserData &user_data) const
|
||||
void pass_through(lf::Params ¶ms,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map) const
|
||||
{
|
||||
std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(params,
|
||||
true);
|
||||
std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(
|
||||
params, data_block_map, true);
|
||||
if (!bake_state) {
|
||||
/* Wait for inputs to be computed. */
|
||||
return;
|
||||
@@ -628,6 +650,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
node_,
|
||||
data_block_map,
|
||||
output_values);
|
||||
for (const int i : simulation_items_.index_range()) {
|
||||
params.output_set(i);
|
||||
@@ -636,6 +659,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
|
||||
void store_new_state(lf::Params ¶ms,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const sim_output::StoreNewState &info) const
|
||||
{
|
||||
const SocketValueVariant *skip_variant =
|
||||
@@ -649,18 +673,18 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
/* Instead of outputting the values directly, convert them to a bake state and then back. This
|
||||
* ensures that some geometry processing happens on the data consistently (e.g. removing
|
||||
* anonymous attributes). */
|
||||
std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(params,
|
||||
skip);
|
||||
std::optional<bke::bake::BakeState> bake_state = this->get_bake_state_from_inputs(
|
||||
params, data_block_map, skip);
|
||||
if (!bake_state) {
|
||||
/* Wait for inputs to be computed. */
|
||||
return;
|
||||
}
|
||||
this->output_cached_state(params, user_data, *bake_state);
|
||||
this->output_cached_state(params, user_data, data_block_map, *bake_state);
|
||||
info.store_fn(std::move(*bake_state));
|
||||
}
|
||||
|
||||
std::optional<bke::bake::BakeState> get_bake_state_from_inputs(lf::Params ¶ms,
|
||||
const bool skip) const
|
||||
std::optional<bke::bake::BakeState> get_bake_state_from_inputs(
|
||||
lf::Params ¶ms, bke::bake::BakeDataBlockMap *data_block_map, const bool skip) const
|
||||
{
|
||||
/* Choose which set of input parameters to use. The others are ignored. */
|
||||
const int params_offset = skip ? skip_inputs_offset_ : solve_inputs_offset_;
|
||||
@@ -673,7 +697,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return move_values_to_simulation_state(simulation_items_, input_values);
|
||||
return move_values_to_simulation_state(simulation_items_, input_values, data_block_map);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -888,6 +912,8 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
|
||||
uiItemR(subcol, &bake_rna, "frame_end", UI_ITEM_NONE, "End", ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
draw_data_blocks(C, layout, bake_rna);
|
||||
}
|
||||
|
||||
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
|
||||
|
||||
@@ -965,8 +965,8 @@ class LazyFunctionForBakeInputsUsage : public LazyFunction {
|
||||
this->set_default_outputs(params);
|
||||
return;
|
||||
}
|
||||
const bool need_inputs = std::holds_alternative<sim_output::PassThrough>(*behavior) ||
|
||||
std::holds_alternative<sim_output::StoreNewState>(*behavior);
|
||||
const bool need_inputs = std::holds_alternative<sim_output::PassThrough>(behavior->behavior) ||
|
||||
std::holds_alternative<sim_output::StoreNewState>(behavior->behavior);
|
||||
params.set_output(0, need_inputs);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user