WIP: Prototype: Geometry Nodes: data block references list per node instance #117635
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-block. 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,7 @@ 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, 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
|
||||
@ -335,6 +336,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,28 @@ 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 char *name, const char *lib_name)
|
||||
{
|
||||
ID *id;
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
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;
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
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.h"
|
||||
#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. */
|
||||
@ -2648,10 +2651,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,19 @@ 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) {
|
||||
MEM_SAFE_FREE(data_block->id_name);
|
||||
MEM_SAFE_FREE(data_block->lib_name);
|
||||
id_us_min(data_block->id);
|
||||
});
|
||||
|
||||
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
|
||||
*bmain, object, nmd, bake_id);
|
||||
if (!bake_path) {
|
||||
|
@ -225,6 +225,7 @@ void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_nodes_input_attribute_toggle(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_node_tree_copy_assign(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_nodes_modifier_data_block_remove(struct wmOperatorType *ot);
|
||||
|
||||
/* object_gpencil_modifiers.c */
|
||||
|
||||
|
@ -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"
|
||||
@ -3724,3 +3725,65 @@ void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot)
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
/** \name Remove Data Block from Geometry Nodes Modifier
|
||||
* \{ */
|
||||
|
||||
static int geometry_nodes_modifier_data_block_remove_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
// Object *ob = ED_object_active_context(C);
|
||||
// Main *bmain = CTX_data_main(C);
|
||||
|
||||
// char modifier_name[MAX_NAME];
|
||||
// RNA_string_get(op->ptr, "modifier_name", modifier_name);
|
||||
// NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(
|
||||
// BKE_modifiers_findby_name(ob, modifier_name));
|
||||
// if (nmd == nullptr) {
|
||||
// return OPERATOR_CANCELLED;
|
||||
// }
|
||||
// if (nmd->data_blocks_num == 0) {
|
||||
// return OPERATOR_CANCELLED;
|
||||
// }
|
||||
// const int index_to_remove = nmd->active_data_block;
|
||||
// if (index_to_remove < 0 || index_to_remove >= nmd->data_blocks_num) {
|
||||
// return OPERATOR_CANCELLED;
|
||||
// }
|
||||
|
||||
// blender::dna::array::remove_index<NodesModifierDataBlock>(
|
||||
// &nmd->data_blocks,
|
||||
// &nmd->data_blocks_num,
|
||||
// &nmd->active_data_block,
|
||||
// index_to_remove,
|
||||
// [](NodesModifierDataBlock *data_block) {
|
||||
// MEM_SAFE_FREE(data_block->id_name);
|
||||
// MEM_SAFE_FREE(data_block->lib_name);
|
||||
// if (data_block->id) {
|
||||
// id_us_min(data_block->id);
|
||||
// }
|
||||
// });
|
||||
|
||||
// DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
// DEG_relations_tag_update(bmain);
|
||||
// WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void OBJECT_OT_geometry_nodes_modifier_data_block_remove(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Remove Geometry Nodes Modifier Data Block";
|
||||
ot->description =
|
||||
"Remove active data block. Note that it might be added right back in case it's used by the "
|
||||
"node tree";
|
||||
ot->idname = "OBJECT_OT_geometry_nodes_modifier_data_block_remove";
|
||||
|
||||
ot->exec = geometry_nodes_modifier_data_block_remove_exec;
|
||||
ot->poll = ED_operator_object_active;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
|
||||
RNA_def_string(ot->srna, "modifier_name", nullptr, MAX_NAME, "Modifier Name", "");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -141,6 +141,7 @@ void ED_operatortypes_object()
|
||||
WM_operatortype_append(OBJECT_OT_skin_armature_create);
|
||||
WM_operatortype_append(OBJECT_OT_geometry_nodes_input_attribute_toggle);
|
||||
WM_operatortype_append(OBJECT_OT_geometry_node_tree_copy_assign);
|
||||
WM_operatortype_append(OBJECT_OT_geometry_nodes_modifier_data_block_remove);
|
||||
|
||||
/* grease pencil modifiers */
|
||||
WM_operatortype_append(OBJECT_OT_gpencil_modifier_add);
|
||||
|
@ -2339,6 +2339,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;
|
||||
@ -2358,6 +2383,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 {
|
||||
@ -2395,6 +2431,7 @@ typedef struct NodesModifierData {
|
||||
char _pad[3];
|
||||
int bakes_num;
|
||||
NodesModifierBake *bakes;
|
||||
|
||||
char _pad2[4];
|
||||
int panels_num;
|
||||
NodesModifierPanel *panels;
|
||||
|
@ -1775,6 +1775,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);
|
||||
@ -7237,8 +7243,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"},
|
||||
@ -7298,6 +7359,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)
|
||||
@ -7338,6 +7404,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);
|
||||
|
||||
@ -7385,6 +7453,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
|
||||
@ -119,6 +120,7 @@ set(LIB
|
||||
PRIVATE bf::depsgraph
|
||||
PUBLIC bf::dna
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
extern_fmtlib
|
||||
)
|
||||
|
||||
if(WITH_ALEMBIC)
|
||||
|
@ -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 everything except `old_mappings` which is read-only during evaluation. */
|
||||
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;
|
||||
|
||||
@ -917,10 +984,15 @@ static bool try_find_baked_data(bake::NodeBakeCache &bake,
|
||||
}
|
||||
|
||||
class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
private:
|
||||
public:
|
||||
static constexpr float max_delta_frames = 1.0f;
|
||||
|
||||
mutable Map<int, std::unique_ptr<nodes::SimulationZoneBehavior>> behavior_by_zone_id_;
|
||||
struct DataPerZone {
|
||||
nodes::SimulationZoneBehavior behavior;
|
||||
NodesModifierBakeDataBlockMap data_block_map;
|
||||
};
|
||||
|
||||
mutable Map<int, std::unique_ptr<DataPerZone>> data_by_zone_id_;
|
||||
const NodesModifierData &nmd_;
|
||||
const ModifierEvalContext &ctx_;
|
||||
const Main *bmain_;
|
||||
@ -1005,26 +1077,38 @@ 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 &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())};
|
||||
const SubFrame sim_end_frame{int(sim_frame_range.last())};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try load baked data. */
|
||||
if (!node_cache.bake.failed_finding_bake) {
|
||||
if (node_cache.cache_status != bake::CacheStatus::Baked) {
|
||||
@ -1229,8 +1313,13 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
};
|
||||
|
||||
class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
|
||||
private:
|
||||
mutable Map<int, std::unique_ptr<nodes::BakeNodeBehavior>> behavior_by_node_id_;
|
||||
public:
|
||||
struct DataPerNode {
|
||||
nodes::BakeNodeBehavior behavior;
|
||||
NodesModifierBakeDataBlockMap data_block_map;
|
||||
};
|
||||
|
||||
mutable Map<int, std::unique_ptr<DataPerNode>> data_by_node_id_;
|
||||
const NodesModifierData &nmd_;
|
||||
const ModifierEvalContext &ctx_;
|
||||
Main *bmain_;
|
||||
@ -1255,27 +1344,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 &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 +1402,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 +1435,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 +1453,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 +1465,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 +1473,151 @@ 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Map<int, NodesModifierBakeDataBlockMap *> data_block_maps;
|
||||
for (auto item : simulation_params.data_by_zone_id_.items()) {
|
||||
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_block_maps.add(item.key, &item.value->data_block_map);
|
||||
}
|
||||
}
|
||||
NodesModifierBake &bake = *nmd_eval.find_bake(item.key);
|
||||
/* The list was reset. */
|
||||
if (item.value->data_block_map.old_mappings.size() < bake.data_blocks_num) {
|
||||
deg::sync_writeback::add(
|
||||
*depsgraph,
|
||||
[depsgraph = depsgraph,
|
||||
object_eval = ctx.object,
|
||||
bmain,
|
||||
&nmd_orig,
|
||||
&nmd_eval,
|
||||
bake_id = item.key]() {
|
||||
NodesModifierBake &bake_orig = *nmd_orig.find_bake(bake_id);
|
||||
NodesModifierBake &bake_eval = *nmd_eval.find_bake(bake_id);
|
||||
|
||||
dna::array::clear<NodesModifierDataBlock>(&bake_orig.data_blocks,
|
||||
&bake_orig.data_blocks_num,
|
||||
&bake_orig.active_data_block,
|
||||
[](NodesModifierDataBlock *data_block) {
|
||||
MEM_SAFE_FREE(data_block->id_name);
|
||||
MEM_SAFE_FREE(data_block->lib_name);
|
||||
id_us_min(data_block->id);
|
||||
});
|
||||
|
||||
dna::array::clear<NodesModifierDataBlock>(&bake_eval.data_blocks,
|
||||
&bake_eval.data_blocks_num,
|
||||
&bake_eval.active_data_block,
|
||||
[](NodesModifierDataBlock *data_block) {
|
||||
MEM_SAFE_FREE(data_block->id_name);
|
||||
MEM_SAFE_FREE(data_block->lib_name);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
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()) {
|
||||
data_block_maps.add(item.key, &item.value->data_block_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto item : data_block_maps.items()) {
|
||||
const int bake_id = item.key;
|
||||
NodesModifierBakeDataBlockMap &data_block_map = *item.value;
|
||||
if (data_block_map.new_mappings.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
deg::sync_writeback::add(
|
||||
*depsgraph,
|
||||
[depsgraph = depsgraph,
|
||||
object_eval = ctx.object,
|
||||
bmain,
|
||||
&nmd_orig,
|
||||
&nmd_eval,
|
||||
bake_id,
|
||||
new_mappings = std::move(data_block_map.new_mappings)]() {
|
||||
NodesModifierBake &bake_orig = *nmd_orig.find_bake(bake_id);
|
||||
NodesModifierBake &bake_eval = *nmd_eval.find_bake(bake_id);
|
||||
|
||||
Vector<bake::BakeDataBlockID> sorted_new_mappings;
|
||||
sorted_new_mappings.extend(new_mappings.keys().begin(), 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 = 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, 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 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 +1709,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
|
||||
@ -1923,7 +2170,10 @@ static void draw_output_attributes_panel(const bContext *C,
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_bake_panel(uiLayout *layout, PointerRNA *modifier_ptr)
|
||||
static void draw_bake_panel(const bContext *C,
|
||||
uiLayout *layout,
|
||||
PointerRNA *modifier_ptr,
|
||||
NodesModifierData &nmd)
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetPropSep(col, true);
|
||||
@ -2005,7 +2255,7 @@ static void draw_manage_panel(const bContext *C,
|
||||
if (uiLayout *panel_layout = uiLayoutPanel(
|
||||
C, layout, IFACE_("Bake"), modifier_ptr, "open_bake_panel"))
|
||||
{
|
||||
draw_bake_panel(panel_layout, modifier_ptr);
|
||||
draw_bake_panel(C, panel_layout, modifier_ptr, nmd);
|
||||
}
|
||||
if (uiLayout *panel_layout = uiLayoutPanel(
|
||||
C, layout, IFACE_("Named Attributes"), modifier_ptr, "open_named_attributes_panel"))
|
||||
@ -2108,6 +2358,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 +2398,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 +2426,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 +2474,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,26 @@ 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,
|
||||
user_data,
|
||||
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 +230,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, user_data, data_block_map);
|
||||
if (!bake_state) {
|
||||
/* Wait for inputs to be computed. */
|
||||
return;
|
||||
@ -239,6 +245,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
output_values[i] = params.get_output_data_ptr(i);
|
||||
}
|
||||
this->move_bake_state_to_values(std::move(*bake_state),
|
||||
user_data,
|
||||
data_block_map,
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
output_values);
|
||||
@ -249,19 +257,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, user_data, 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 +280,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
output_values[i] = params.get_output_data_ptr(i);
|
||||
}
|
||||
this->copy_bake_state_to_values(bake_state,
|
||||
user_data,
|
||||
data_block_map,
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
output_values);
|
||||
@ -278,6 +291,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
}
|
||||
|
||||
void output_mixed_cached_state(lf::Params ¶ms,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bake::BakeStateRef &prev_state,
|
||||
@ -288,7 +303,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, user_data, data_block_map, self_object, compute_context, output_values);
|
||||
|
||||
Array<void *> next_values(bake_items_.size());
|
||||
LinearAllocator<> allocator;
|
||||
@ -296,7 +312,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, user_data, 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 +332,10 @@ 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,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map) const
|
||||
{
|
||||
Array<void *> input_values(bake_items_.size());
|
||||
for (const int i : bake_items_.index_range()) {
|
||||
@ -327,7 +347,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 +361,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
}
|
||||
|
||||
void move_bake_state_to_values(bake::BakeState bake_state,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
Span<void *> r_output_values) const
|
||||
@ -354,6 +376,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 +384,8 @@ class LazyFunctionForBakeNode final : public LazyFunction {
|
||||
}
|
||||
|
||||
void copy_bake_state_to_values(const bake::BakeStateRef &bake_state,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
Span<void *> r_output_values) const
|
||||
@ -373,6 +398,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 +617,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 +641,63 @@ 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 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);
|
||||
|
||||
{
|
||||
uiLayout *row = uiLayoutRow(layout, false);
|
||||
uiTemplateList(row,
|
||||
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,12 @@ 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,
|
||||
user_data,
|
||||
zone_behavior->data_block_map,
|
||||
*user_data.call_data->self_object(),
|
||||
*user_data.compute_context,
|
||||
info->prev_state,
|
||||
@ -536,10 +544,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 +561,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 +573,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 +581,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
||||
}
|
||||
|
||||
void output_mixed_cached_state(lf::Params ¶ms,
|
||||
GeoNodesLFUserData &user_data,
|
||||
bke::bake::BakeDataBlockMap *data_block_map,
|
||||
const Object &self_object,
|
||||
const ComputeContext &compute_context,
|
||||
const bke::bake::BakeStateRef &prev_state,
|
||||
@ -581,8 +593,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 +607,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 +632,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 +652,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 +661,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 +675,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 +699,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 +914,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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user