Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPiotr Makal <pmakal>2022-05-06 12:36:27 +0300
committerSybren A. Stüvel <sybren@blender.org>2022-05-06 12:43:43 +0300
commitce3dd12371f5bd02eb64488712d8c529ffe62f6b (patch)
treef489357ce47ec4fa1e2ba0fb02a8f8a11955192d /source/blender/io/usd/intern
parentf3b56246d1ef113a434cc647c6865f91631bc18d (diff)
USD: add volume/VDB export
Add support for volume (OpenVDB) USD export: - Allows to export both static and animated volumes. - Supports volumes that have OpenVDB data from files or are generated in Blender with 'Mesh to Volume' modifier. - For volumes that have generated data in Blender it also exports corresponding .vdb files. Those files are saved in a new folder named "volumes". - Slightly changes the USD export UI panel. "Relative Texture Paths" becomes "Relative Paths" (and has separate UI box) as the functionality will now apply to both textures and volumes. Disabling of this option due to "Materials" checkbox being turned off has been removed. Reviewed By: sybren, makowalski Differential Revision: https://developer.blender.org/D14193 Manifest Task: T95407
Diffstat (limited to 'source/blender/io/usd/intern')
-rw-r--r--source/blender/io/usd/intern/usd_capi_export.cc2
-rw-r--r--source/blender/io/usd/intern/usd_exporter_context.h2
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.cc21
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.h5
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.cc5
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.h1
-rw-r--r--source/blender/io/usd/intern/usd_writer_material.cc10
-rw-r--r--source/blender/io/usd/intern/usd_writer_volume.cc188
-rw-r--r--source/blender/io/usd/intern/usd_writer_volume.h36
9 files changed, 258 insertions, 12 deletions
diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc
index d7c1a5adc7c..c52d791f3b3 100644
--- a/source/blender/io/usd/intern/usd_capi_export.cc
+++ b/source/blender/io/usd/intern/usd_capi_export.cc
@@ -97,7 +97,7 @@ static void export_startjob(void *customdata,
usd_stage->SetEndTimeCode(scene->r.efra);
}
- USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params);
+ USDHierarchyIterator iter(data->bmain, data->depsgraph, usd_stage, data->params);
if (data->params.export_animation) {
/* Writing the animated frames is not 100% of the work, but it's our best guess. */
diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h
index 5a3f94d01f8..a636d849296 100644
--- a/source/blender/io/usd/intern/usd_exporter_context.h
+++ b/source/blender/io/usd/intern/usd_exporter_context.h
@@ -8,12 +8,14 @@
#include <pxr/usd/usd/common.h>
struct Depsgraph;
+struct Main;
namespace blender::io::usd {
class USDHierarchyIterator;
struct USDExporterContext {
+ Main *bmain;
Depsgraph *depsgraph;
const pxr::UsdStageRefPtr stage;
const pxr::SdfPath usd_path;
diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
index 7a0d896fb3e..51261c4d91e 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
@@ -10,6 +10,7 @@
#include "usd_writer_mesh.h"
#include "usd_writer_metaball.h"
#include "usd_writer_transform.h"
+#include "usd_writer_volume.h"
#include <string>
@@ -28,10 +29,11 @@
namespace blender::io::usd {
-USDHierarchyIterator::USDHierarchyIterator(Depsgraph *depsgraph,
+USDHierarchyIterator::USDHierarchyIterator(Main *bmain,
+ Depsgraph *depsgraph,
pxr::UsdStageRefPtr stage,
const USDExportParams &params)
- : AbstractHierarchyIterator(depsgraph), stage_(stage), params_(params)
+ : AbstractHierarchyIterator(bmain, depsgraph), stage_(stage), params_(params)
{
}
@@ -59,6 +61,15 @@ void USDHierarchyIterator::set_export_frame(float frame_nr)
export_time_ = pxr::UsdTimeCode(frame_nr);
}
+std::string USDHierarchyIterator::get_export_file_path() const
+{
+ /* Returns the same path that was passed to `stage_` object during it's creation (via
+ * `pxr::UsdStage::CreateNew` function). */
+ const pxr::SdfLayerHandle root_layer = stage_->GetRootLayer();
+ const std::string usd_export_file_path = root_layer->GetRealPath();
+ return usd_export_file_path;
+}
+
const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const
{
return export_time_;
@@ -66,7 +77,8 @@ const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const
USDExporterContext USDHierarchyIterator::create_usd_export_context(const HierarchyContext *context)
{
- return USDExporterContext{depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_};
+ return USDExporterContext{
+ bmain_, depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_};
}
AbstractHierarchyWriter *USDHierarchyIterator::create_transform_writer(
@@ -93,6 +105,9 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const Hierarch
case OB_MBALL:
data_writer = new USDMetaballWriter(usd_export_context);
break;
+ case OB_VOLUME:
+ data_writer = new USDVolumeWriter(usd_export_context);
+ break;
case OB_EMPTY:
case OB_CURVES_LEGACY:
diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h
index d5566103af4..445c37238be 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h
@@ -12,6 +12,7 @@
#include <pxr/usd/usd/timeCode.h>
struct Depsgraph;
+struct Main;
struct Object;
namespace blender::io::usd {
@@ -27,11 +28,13 @@ class USDHierarchyIterator : public AbstractHierarchyIterator {
const USDExportParams &params_;
public:
- USDHierarchyIterator(Depsgraph *depsgraph,
+ USDHierarchyIterator(Main *bmain,
+ Depsgraph *depsgraph,
pxr::UsdStageRefPtr stage,
const USDExportParams &params);
void set_export_frame(float frame_nr);
+ std::string get_export_file_path() const;
const pxr::UsdTimeCode &get_export_time_code() const;
virtual std::string make_valid_name(const std::string &name) const override;
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc
index dbeb9df1ee8..2be9b1c065a 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.cc
+++ b/source/blender/io/usd/intern/usd_writer_abstract.cc
@@ -47,6 +47,11 @@ bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const
return true;
}
+std::string USDAbstractWriter::get_export_file_path() const
+{
+ return usd_export_context_.hierarchy_iterator->get_export_file_path();
+}
+
pxr::UsdTimeCode USDAbstractWriter::get_export_time_code() const
{
if (is_animated_) {
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h
index 66feb6e1070..8adc054c2c2 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.h
+++ b/source/blender/io/usd/intern/usd_writer_abstract.h
@@ -51,6 +51,7 @@ class USDAbstractWriter : public AbstractHierarchyWriter {
protected:
virtual void do_write(HierarchyContext &context) = 0;
+ std::string get_export_file_path() const;
pxr::UsdTimeCode get_export_time_code() const;
pxr::UsdShadeMaterial ensure_usd_material(const HierarchyContext &context, Material *material);
diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc
index 1bfc0e50f69..a24877a20bd 100644
--- a/source/blender/io/usd/intern/usd_writer_material.cc
+++ b/source/blender/io/usd/intern/usd_writer_material.cc
@@ -576,7 +576,7 @@ static std::string get_tex_image_asset_path(bNode *node,
char file_path[FILE_MAX];
BLI_split_file_part(path.c_str(), file_path, FILE_MAX);
- if (export_params.relative_texture_paths) {
+ if (export_params.relative_paths) {
BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path, nullptr);
}
else {
@@ -594,7 +594,7 @@ static std::string get_tex_image_asset_path(bNode *node,
return exp_path;
}
- if (export_params.relative_texture_paths) {
+ if (export_params.relative_paths) {
/* Get the path relative to the USD. */
pxr::SdfLayerHandle layer = stage->GetRootLayer();
std::string stage_path = layer->GetRealPath();
@@ -606,11 +606,7 @@ static std::string get_tex_image_asset_path(bNode *node,
strcpy(rel_path, path.c_str());
BLI_path_rel(rel_path, stage_path.c_str());
-
- /* BLI_path_rel adds '//' as a prefix to the path, if
- * generating the relative path was successful. */
- if (rel_path[0] != '/' || rel_path[1] != '/') {
- /* No relative path generated. */
+ if (!BLI_path_is_rel(rel_path)) {
return path;
}
diff --git a/source/blender/io/usd/intern/usd_writer_volume.cc b/source/blender/io/usd/intern/usd_writer_volume.cc
new file mode 100644
index 00000000000..b83ded1e5d0
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_writer_volume.cc
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "usd_writer_volume.h"
+
+#include <pxr/base/tf/pathUtils.h>
+#include <pxr/usd/usdVol/openVDBAsset.h>
+#include <pxr/usd/usdVol/volume.h>
+
+#include "DNA_volume_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_volume.h"
+
+#include "BLI_fileops.h"
+#include "BLI_index_range.hh"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "WM_api.h"
+
+#include "usd_hierarchy_iterator.h"
+
+namespace blender::io::usd {
+
+USDVolumeWriter::USDVolumeWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx)
+{
+}
+
+bool USDVolumeWriter::check_is_animated(const HierarchyContext &context) const
+{
+ const Volume *volume = static_cast<Volume *>(context.object->data);
+ return volume->is_sequence;
+}
+
+void USDVolumeWriter::do_write(HierarchyContext &context)
+{
+ Volume *volume = static_cast<Volume *>(context.object->data);
+ if (!BKE_volume_load(volume, usd_export_context_.bmain)) {
+ return;
+ }
+
+ const int num_grids = BKE_volume_num_grids(volume);
+ if (!num_grids) {
+ return;
+ }
+
+ auto vdb_file_path = resolve_vdb_file(volume);
+ if (!vdb_file_path.has_value()) {
+ WM_reportf(RPT_WARNING,
+ "USD Export: failed to resolve .vdb file for object: %s",
+ volume->id.name + 2);
+ return;
+ }
+
+ if (usd_export_context_.export_params.relative_paths) {
+ if (auto relative_vdb_file_path = construct_vdb_relative_file_path(vdb_file_path.value())) {
+ vdb_file_path = relative_vdb_file_path;
+ }
+ else {
+ WM_reportf(RPT_WARNING,
+ "USD Export: couldn't construct relative file path for .vdb file, absolute path "
+ "will be used instead");
+ }
+ }
+
+ const pxr::UsdTimeCode timecode = get_export_time_code();
+ const pxr::SdfPath &volume_path = usd_export_context_.usd_path;
+ pxr::UsdStageRefPtr stage = usd_export_context_.stage;
+ pxr::UsdVolVolume usd_volume = pxr::UsdVolVolume::Define(stage, volume_path);
+
+ for (const int i : IndexRange(num_grids)) {
+ const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
+ const std::string grid_name = BKE_volume_grid_name(grid);
+ const std::string grid_id = pxr::TfMakeValidIdentifier(grid_name);
+ const pxr::SdfPath grid_path = volume_path.AppendPath(pxr::SdfPath(grid_id));
+ pxr::UsdVolOpenVDBAsset usd_grid = pxr::UsdVolOpenVDBAsset::Define(stage, grid_path);
+ usd_grid.GetFieldNameAttr().Set(pxr::TfToken(grid_name), timecode);
+ usd_grid.GetFilePathAttr().Set(pxr::SdfAssetPath(vdb_file_path.value()), timecode);
+ usd_volume.CreateFieldRelationship(pxr::TfToken(grid_id), grid_path);
+ }
+
+ float3 volume_bound_min(std::numeric_limits<float>::max());
+ float3 volume_bound_max(std::numeric_limits<float>::min());
+ if (BKE_volume_min_max(volume, volume_bound_min, volume_bound_max)) {
+ const pxr::VtArray<pxr::GfVec3f> volume_extent = {pxr::GfVec3f(&volume_bound_min[0]),
+ pxr::GfVec3f(&volume_bound_max[0])};
+ usd_volume.GetExtentAttr().Set(volume_extent, timecode);
+ }
+
+ BKE_volume_unload(volume);
+}
+
+std::optional<std::string> USDVolumeWriter::resolve_vdb_file(const Volume *volume) const
+{
+ std::optional<std::string> vdb_file_path;
+ if (volume->filepath[0] == '\0') {
+ /* Entering this section should mean that Volume object contains OpenVDB data that is not
+ * obtained from external .vdb file but rather generated inside of Blender (i.e. by 'Mesh to
+ * Volume' modifier). Try to save this data to a .vdb file. */
+
+ vdb_file_path = construct_vdb_file_path(volume);
+ if (!BKE_volume_save(
+ volume, usd_export_context_.bmain, NULL, vdb_file_path.value_or("").c_str())) {
+ return std::nullopt;
+ }
+ }
+
+ if (!vdb_file_path.has_value()) {
+ vdb_file_path = BKE_volume_grids_frame_filepath(volume);
+ if (vdb_file_path.value().empty()) {
+ return std::nullopt;
+ }
+ }
+
+ return vdb_file_path;
+}
+
+std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume *volume) const
+{
+ const std::string usd_file_path = get_export_file_path();
+ if (usd_file_path.empty()) {
+ return std::nullopt;
+ }
+
+ char usd_directory_path[FILE_MAX];
+ char usd_file_name[FILE_MAXFILE];
+ BLI_split_dirfile(usd_file_path.c_str(),
+ usd_directory_path,
+ usd_file_name,
+ sizeof(usd_directory_path),
+ sizeof(usd_file_name));
+
+ if (usd_directory_path[0] == '\0' || usd_file_name[0] == '\0') {
+ return std::nullopt;
+ }
+
+ const char *vdb_directory_name = "volumes";
+
+ char vdb_directory_path[FILE_MAX];
+ BLI_strncpy(vdb_directory_path, usd_directory_path, FILE_MAX);
+ strcat(vdb_directory_path, vdb_directory_name);
+ BLI_dir_create_recursive(vdb_directory_path);
+
+ char vdb_file_name[FILE_MAXFILE];
+ BLI_strncpy(vdb_file_name, volume->id.name + 2, FILE_MAXFILE);
+ const pxr::UsdTimeCode timecode = get_export_time_code();
+ if (!timecode.IsDefault()) {
+ const int frame = (int)timecode.GetValue();
+ const int num_frame_digits = frame == 0 ? 1 : integer_digits_i(abs(frame));
+ BLI_path_frame(vdb_file_name, frame, num_frame_digits);
+ }
+ strcat(vdb_file_name, ".vdb");
+
+ char vdb_file_path[FILE_MAX];
+ BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, NULL);
+
+ return vdb_file_path;
+}
+
+std::optional<std::string> USDVolumeWriter::construct_vdb_relative_file_path(
+ const std::string &vdb_file_path) const
+{
+ const std::string usd_file_path = get_export_file_path();
+ if (usd_file_path.empty()) {
+ return std::nullopt;
+ }
+
+ char relative_path[FILE_MAX];
+ BLI_strncpy(relative_path, vdb_file_path.c_str(), FILE_MAX);
+ BLI_path_rel(relative_path, usd_file_path.c_str());
+ if (!BLI_path_is_rel(relative_path)) {
+ return std::nullopt;
+ }
+
+ /* Following code was written with an assumption that Blender's relative paths start with
+ * // characters as well as have OS dependent slashes. Inside of USD files those relative
+ * paths should start with either ./ or ../ characters and have always forward slashes (/)
+ * separating directories. This is the convention used in USD documentation (and it seems
+ * to be used in other DCC packages as well). */
+ std::string relative_path_processed = pxr::TfNormPath(relative_path + 2);
+ if (relative_path_processed[0] != '.') {
+ relative_path_processed.insert(0, "./");
+ }
+
+ return relative_path_processed;
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_writer_volume.h b/source/blender/io/usd/intern/usd_writer_volume.h
new file mode 100644
index 00000000000..8c1e36b7e53
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_writer_volume.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#pragma once
+
+#include <optional>
+
+#include "BLI_math_vec_types.hh"
+#include "usd_writer_abstract.h"
+
+struct Volume;
+
+namespace blender::io::usd {
+
+/* Writer for writing OpenVDB assets to UsdVolVolume. Volume data is stored in separate .vdb files
+ * which are referenced in USD file. */
+class USDVolumeWriter : public USDAbstractWriter {
+ public:
+ USDVolumeWriter(const USDExporterContext &ctx);
+
+ protected:
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+ virtual void do_write(HierarchyContext &context) override;
+
+ private:
+ /* Try to ensure that external .vdb file is available for USD to be referenced. Blender can
+ * either reference external OpenVDB data or generate such data internally. Latter option will
+ * mean that `resolve_vdb_file` method will try to export volume data to a new .vdb file. If
+ * successful, this method returns absolute file path to the resolved .vdb file, if not, returns
+ * `std::nullopt`. */
+ std::optional<std::string> resolve_vdb_file(const Volume *volume) const;
+
+ std::optional<std::string> construct_vdb_file_path(const Volume *volume) const;
+ std::optional<std::string> construct_vdb_relative_file_path(
+ const std::string &vdb_file_path) const;
+};
+
+} // namespace blender::io::usd