WIP: Geometry nodes: Basic volume simulation support #108468

Closed
Erik Abrahamsson wants to merge 3 commits from erik85/blender:sim-vol into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
4 changed files with 112 additions and 7 deletions

View File

@ -53,6 +53,8 @@ class BDataWriter {
* \return Slice where the data has been written to.
*/
virtual BDataSlice write(const void *data, int64_t size) = 0;
[[nodiscard]] virtual std::ostream &stream() = 0;
[[nodiscard]] virtual std::string &name() = 0;
};
/**
@ -124,6 +126,7 @@ class DiskBDataReader : public BDataReader {
public:
DiskBDataReader(std::string bdata_dir);
[[nodiscard]] bool read(const BDataSlice &slice, void *r_data) const override;
[[nodiscard]] const std::string &dir() const;
};
/**
@ -142,6 +145,8 @@ class DiskBDataWriter : public BDataWriter {
DiskBDataWriter(std::string bdata_name, std::ostream &bdata_file, int64_t current_offset);
BDataSlice write(const void *data, int64_t size) override;
[[nodiscard]] std::ostream &stream() override;
[[nodiscard]] std::string &name() override;
};
/**
@ -157,6 +162,7 @@ std::string get_default_modifier_bake_directory(const Main &bmain,
*/
void serialize_modifier_simulation_state(const ModifierSimulationState &state,
BDataWriter &bdata_writer,
BDataWriter &vdb_writer,
BDataSharing &bdata_sharing,
DictionaryValue &r_io_root);
/**

View File

@ -3,16 +3,19 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_curves.hh"
#include "BKE_global.h"
#include "BKE_instances.hh"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_mesh.hh"
#include "BKE_pointcloud.h"
#include "BKE_simulation_state_serialize.hh"
#include "BKE_volume.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_volume_types.h"
#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
@ -23,6 +26,11 @@
#include "RNA_access.h"
#include "RNA_enum_types.h"
#ifdef WITH_OPENVDB
# include <openvdb/io/Stream.h>
# include <openvdb/openvdb.h>
#endif
namespace blender::bke::sim {
/**
@ -502,6 +510,41 @@ static Mesh *try_load_mesh(const DictionaryValue &io_geometry,
return mesh;
}
static Volume *try_load_volume(const DictionaryValue &io_geometry, const BDataReader &vdb_reader)
{
const DictionaryValue *io_volume = io_geometry.lookup_dict("volume");
if (!io_volume) {
return nullptr;
}
const std::optional<StringRefNull> vdb_name = io_volume->lookup_str("name");
if (!vdb_name) {
return nullptr;
}
const DiskBDataReader *disk_reader = dynamic_cast<const DiskBDataReader *>(&vdb_reader);
if (disk_reader == nullptr) {
return nullptr;
}
char vdb_path[FILE_MAX];
BLI_path_join(vdb_path, sizeof(vdb_path), disk_reader->dir().c_str(), vdb_name.value().c_str());
Volume *volume = static_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr));
STRNCPY(volume->filepath, vdb_path);
auto cancel = [&]() {
BKE_id_free(nullptr, volume);
return nullptr;
};
if (!BKE_volume_load(volume, G.main)) {
return cancel();
}
return volume;
}
static GeometrySet load_geometry(const DictionaryValue &io_geometry,
const BDataReader &bdata_reader,
const BDataSharing &bdata_sharing);
@ -571,6 +614,7 @@ static GeometrySet load_geometry(const DictionaryValue &io_geometry,
geometry.replace_mesh(try_load_mesh(io_geometry, bdata_reader, bdata_sharing));
geometry.replace_pointcloud(try_load_pointcloud(io_geometry, bdata_reader, bdata_sharing));
geometry.replace_curves(try_load_curves(io_geometry, bdata_reader, bdata_sharing));
geometry.replace_volume(try_load_volume(io_geometry, bdata_reader));
geometry.replace_instances(
try_load_instances(io_geometry, bdata_reader, bdata_sharing).release());
return geometry;
@ -634,6 +678,7 @@ static std::shared_ptr<io::serialize::ArrayValue> serialize_attributes(
static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet &geometry,
BDataWriter &bdata_writer,
BDataWriter &vdb_writer,
BDataSharing &bdata_sharing)
{
auto io_geometry = std::make_shared<DictionaryValue>();
@ -698,6 +743,29 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
curves.attributes(), bdata_writer, bdata_sharing, {});
io_curves->append("attributes", io_attributes);
}
#ifdef WITH_OPENVDB
if (geometry.has_volume()) {
const Volume &volume = *geometry.get_volume_for_read();

This can throw exceptions that need to be caught, see BKE_volume_save.

This can throw exceptions that need to be caught, see `BKE_volume_save`.
openvdb::GridCPtrVec grids;
for (const int i : IndexRange(BKE_volume_num_grids(&volume))) {
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(&volume, i);
openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(&volume, volume_grid);
grids.push_back(grid);
}
try {
std::ostream &stream = vdb_writer.stream();
openvdb::io::Stream file(stream);
file.write(grids);
auto io_volume = io_geometry->append_dict("volume");
io_volume->append_str("name", vdb_writer.name());
}
catch (...) {
}
}
#endif
if (geometry.has_instances()) {
const bke::Instances &instances = *geometry.get_instances_for_read();
auto io_instances = io_geometry->append_dict("instances");
@ -707,8 +775,8 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
auto io_references = io_instances->append_array("references");
for (const bke::InstanceReference &reference : instances.references()) {
BLI_assert(reference.type() == bke::InstanceReference::Type::GeometrySet);
io_references->append(
serialize_geometry_set(reference.geometry_set(), bdata_writer, bdata_sharing));
io_references->append(serialize_geometry_set(
reference.geometry_set(), bdata_writer, vdb_writer, bdata_sharing));
}
io_instances->append("transforms",
@ -787,6 +855,7 @@ static std::shared_ptr<io::serialize::Value> serialize_primitive_value(
void serialize_modifier_simulation_state(const ModifierSimulationState &state,
BDataWriter &bdata_writer,
BDataWriter &vdb_writer,
BDataSharing &bdata_sharing,
DictionaryValue &r_io_root)
{
@ -819,7 +888,8 @@ void serialize_modifier_simulation_state(const ModifierSimulationState &state,
io_state_item->append_str("type", "GEOMETRY");
const GeometrySet &geometry = geometry_state_item->geometry;
auto io_geometry = serialize_geometry_set(geometry, bdata_writer, bdata_sharing);
auto io_geometry = serialize_geometry_set(
geometry, bdata_writer, vdb_writer, bdata_sharing);
io_state_item->append("data", io_geometry);
}
else if (const AttributeSimulationStateItem *attribute_state_item =
@ -1056,7 +1126,8 @@ void deserialize_modifier_simulation_state(const DictionaryValue &io_root,
io_string.value());
}
else if (const io::serialize::DictionaryValue *io_string =
io_data->get()->as_dictionary_value()) {
io_data->get()->as_dictionary_value())
{
const std::optional<int64_t> size = io_string->lookup_int("size");
if (!size) {
continue;
@ -1117,6 +1188,11 @@ DiskBDataReader::DiskBDataReader(std::string bdata_dir) : bdata_dir_(std::move(b
return true;
}
[[nodiscard]] const std::string &DiskBDataReader::dir() const
{
return bdata_dir_;
}
DiskBDataWriter::DiskBDataWriter(std::string bdata_name,
std::ostream &bdata_file,
const int64_t current_offset)
@ -1132,6 +1208,16 @@ BDataSlice DiskBDataWriter::write(const void *data, const int64_t size)
return {bdata_name_, {old_offset, size}};
}
[[nodiscard]] std::ostream &DiskBDataWriter::stream()
{
return bdata_file_;
}
[[nodiscard]] std::string &DiskBDataWriter::name()
{
return bdata_name_;
}
BDataSharing::~BDataSharing()
{
for (const ImplicitSharingInfo *sharing_info : stored_by_runtime_.keys()) {

View File

@ -309,6 +309,7 @@ static void bake_simulation_job_startjob(void *customdata,
const std::string bdata_file_name = frame_file_str + ".bdata";
const std::string meta_file_name = frame_file_str + ".json";
const std::string vdb_file_name = frame_file_str + ".vdb";
char bdata_path[FILE_MAX];
BLI_path_join(bdata_path,
@ -323,13 +324,23 @@ static void bake_simulation_job_startjob(void *customdata,
"meta",
meta_file_name.c_str());
char vdb_path[FILE_MAX];
BLI_path_join(vdb_path,
sizeof(vdb_path),
modifier_bake_data.absolute_bake_dir.c_str(),
"bdata",
vdb_file_name.c_str());
BLI_file_ensure_parent_dir_exists(bdata_path);
fstream bdata_file{bdata_path, std::ios::out | std::ios::binary};
bke::sim::DiskBDataWriter bdata_writer{bdata_file_name, bdata_file, 0};
fstream vdb_file{vdb_path, std::ios::out | std::ios::binary};
bke::sim::DiskBDataWriter vdb_writer{vdb_file_name, vdb_file, 0};
io::serialize::DictionaryValue io_root;
bke::sim::serialize_modifier_simulation_state(
*sim_state, bdata_writer, *modifier_bake_data.bdata_sharing, io_root);
*sim_state, bdata_writer, vdb_writer, *modifier_bake_data.bdata_sharing, io_root);
BLI_file_ensure_parent_dir_exists(meta_path);
io::serialize::write_json_file(meta_path, io_root);

View File

@ -167,7 +167,8 @@ static void cleanup_geometry_for_simulation_state(GeometrySet &main_geometry)
geometry.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH,
GEO_COMPONENT_TYPE_CURVE,
GEO_COMPONENT_TYPE_POINT_CLOUD,
GEO_COMPONENT_TYPE_INSTANCES});
GEO_COMPONENT_TYPE_INSTANCES,
GEO_COMPONENT_TYPE_VOLUME});
});
}
@ -279,7 +280,8 @@ void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_i
GeometryComponent &component = geometry->get_component_for_write(type);
MutableAttributeAccessor attributes = *component.attributes_for_write();
for (const MapItem<std::string, AnonymousAttributeIDPtr> &attribute_item :
attribute_map.items()) {
attribute_map.items())
{
attributes.rename(attribute_item.key, *attribute_item.value);
}
}