Blender V4.3
usd_writer_volume.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
7#include "usd_utils.hh"
8
9#include <pxr/base/tf/pathUtils.h>
10#include <pxr/usd/usdVol/openVDBAsset.h>
11#include <pxr/usd/usdVol/volume.h>
12
13#include "DNA_volume_types.h"
15
16#include "BKE_report.hh"
17#include "BKE_volume.hh"
18
19#include "BLI_fileops.h"
20#include "BLI_index_range.hh"
21#include "BLI_math_base.h"
23#include "BLI_path_utils.hh"
24#include "BLI_string.h"
25
26namespace blender::io::usd {
27
29
31{
32 const Volume *volume = static_cast<Volume *>(context.object->data);
33 return volume->is_sequence;
34}
35
37{
38 Volume *volume = static_cast<Volume *>(context.object->data);
40 return;
41 }
42
43 const int num_grids = BKE_volume_num_grids(volume);
44 if (!num_grids) {
45 return;
46 }
47
48 auto vdb_file_path = resolve_vdb_file(volume);
49 if (!vdb_file_path.has_value()) {
52 "USD Export: failed to resolve .vdb file for object: %s",
53 volume->id.name + 2);
54 return;
55 }
56
58 if (auto relative_vdb_file_path = construct_vdb_relative_file_path(*vdb_file_path)) {
59 vdb_file_path = relative_vdb_file_path;
60 }
61 else {
64 "USD Export: couldn't construct relative file path for .vdb file, absolute path "
65 "will be used instead");
66 }
67 }
68
69 const pxr::UsdTimeCode timecode = get_export_time_code();
70 const pxr::SdfPath &volume_path = usd_export_context_.usd_path;
71 pxr::UsdStageRefPtr stage = usd_export_context_.stage;
72 pxr::UsdVolVolume usd_volume = pxr::UsdVolVolume::Define(stage, volume_path);
73
74 for (const int i : IndexRange(num_grids)) {
75 const bke::VolumeGridData *grid = BKE_volume_grid_get(volume, i);
76 const std::string grid_name = bke::volume_grid::get_name(*grid);
77 const std::string grid_id = make_safe_name(grid_name,
79 const pxr::SdfPath grid_path = volume_path.AppendPath(pxr::SdfPath(grid_id));
80 pxr::UsdVolOpenVDBAsset usd_grid = pxr::UsdVolOpenVDBAsset::Define(stage, grid_path);
81 usd_grid.GetFieldNameAttr().Set(pxr::TfToken(grid_name), timecode);
82 usd_grid.GetFilePathAttr().Set(pxr::SdfAssetPath(*vdb_file_path), timecode);
83 usd_volume.CreateFieldRelationship(pxr::TfToken(grid_id), grid_path);
84 }
85
86 if (const std::optional<Bounds<float3>> bounds = BKE_volume_min_max(volume)) {
87 const pxr::VtArray<pxr::GfVec3f> volume_extent = {pxr::GfVec3f(&bounds->min.x),
88 pxr::GfVec3f(&bounds->max.x)};
89 usd_volume.GetExtentAttr().Set(volume_extent, timecode);
90 }
91
92 BKE_volume_unload(volume);
93}
94
95std::optional<std::string> USDVolumeWriter::resolve_vdb_file(const Volume *volume) const
96{
97 std::optional<std::string> vdb_file_path;
98 if (volume->filepath[0] == '\0') {
99 /* Entering this section should mean that Volume object contains OpenVDB data that is not
100 * obtained from external `.vdb` file but rather generated inside of Blender (i.e. by 'Mesh to
101 * Volume' modifier). Try to save this data to a `.vdb` file. */
102
103 vdb_file_path = construct_vdb_file_path(volume);
104 if (!BKE_volume_save(
105 volume, usd_export_context_.bmain, nullptr, vdb_file_path.value_or("").c_str()))
106 {
107 return std::nullopt;
108 }
109 }
110
111 if (!vdb_file_path.has_value()) {
112 vdb_file_path = BKE_volume_grids_frame_filepath(volume);
113 if (vdb_file_path->empty()) {
114 return std::nullopt;
115 }
116 }
117
118 return vdb_file_path;
119}
120
121std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume *volume) const
122{
123 const std::string usd_file_path = get_export_file_path();
124 if (usd_file_path.empty()) {
125 return std::nullopt;
126 }
127
128 char usd_directory_path[FILE_MAX];
129 char usd_file_name[FILE_MAXFILE];
130 BLI_path_split_dir_file(usd_file_path.c_str(),
131 usd_directory_path,
132 sizeof(usd_directory_path),
133 usd_file_name,
134 sizeof(usd_file_name));
135
136 if (usd_directory_path[0] == '\0' || usd_file_name[0] == '\0') {
137 return std::nullopt;
138 }
139
140 const char *vdb_directory_name = "volumes";
141
142 char vdb_directory_path[FILE_MAX];
143 STRNCPY(vdb_directory_path, usd_directory_path);
144 BLI_strncat(vdb_directory_path, vdb_directory_name, sizeof(vdb_directory_path));
145 BLI_dir_create_recursive(vdb_directory_path);
146
147 char vdb_file_name[FILE_MAXFILE];
148 STRNCPY(vdb_file_name, volume->id.name + 2);
149 const pxr::UsdTimeCode timecode = get_export_time_code();
150 if (!timecode.IsDefault()) {
151 const int frame = int(timecode.GetValue());
152 const int num_frame_digits = frame == 0 ? 1 : integer_digits_i(abs(frame));
153 BLI_path_frame(vdb_file_name, sizeof(vdb_file_name), frame, num_frame_digits);
154 }
155 BLI_strncat(vdb_file_name, ".vdb", sizeof(vdb_file_name));
156
157 char vdb_file_path[FILE_MAX];
158 BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name);
159
160 return vdb_file_path;
161}
162
163std::optional<std::string> USDVolumeWriter::construct_vdb_relative_file_path(
164 const std::string &vdb_file_path) const
165{
166 const std::string usd_file_path = get_export_file_path();
167 if (usd_file_path.empty()) {
168 return std::nullopt;
169 }
170
171 char relative_path[FILE_MAX];
172 STRNCPY(relative_path, vdb_file_path.c_str());
173 BLI_path_rel(relative_path, usd_file_path.c_str());
174 if (!BLI_path_is_rel(relative_path)) {
175 return std::nullopt;
176 }
177
178 /* Following code was written with an assumption that Blender's relative paths start with
179 * // characters as well as have OS dependent slashes. Inside of USD files those relative
180 * paths should start with either ./ or ../ characters and have always forward slashes (/)
181 * separating directories. This is the convention used in USD documentation (and it seems
182 * to be used in other DCC packages as well). */
183 std::string relative_path_processed = pxr::TfNormPath(relative_path + 2);
184 if (relative_path_processed[0] != '.') {
185 relative_path_processed.insert(0, "./");
186 }
187
188 return relative_path_processed;
189}
190
191} // namespace blender::io::usd
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
Volume data-block.
int BKE_volume_num_grids(const Volume *volume)
bool BKE_volume_save(const Volume *volume, const Main *bmain, ReportList *reports, const char *filepath)
bool BKE_volume_load(const Volume *volume, const Main *bmain)
std::optional< blender::Bounds< blender::float3 > > BKE_volume_min_max(const Volume *volume)
const char * BKE_volume_grids_frame_filepath(const Volume *volume)
const blender::bke::VolumeGridData * BKE_volume_grid_get(const Volume *volume, int grid_index)
void BKE_volume_unload(Volume *volume)
File and directory operations.
bool BLI_dir_create_recursive(const char *dirname) ATTR_NONNULL()
Definition fileops_c.cc:391
MINLINE int integer_digits_i(int i)
#define FILE_MAXFILE
#define FILE_MAX
#define BLI_path_join(...)
void BLI_path_split_dir_file(const char *filepath, char *dir, size_t dir_maxncpy, char *file, size_t file_maxncpy) ATTR_NONNULL(1
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits) ATTR_NONNULL(1)
#define STRNCPY(dst, src)
Definition BLI_string.h:593
char char size_t char * BLI_strncat(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
pxr::UsdTimeCode get_export_time_code() const
const USDExporterContext usd_export_context_
virtual bool check_is_animated(const HierarchyContext &context) const override
virtual void do_write(HierarchyContext &context) override
USDVolumeWriter(const USDExporterContext &ctx)
EvaluationStage stage
Definition deg_eval.cc:83
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
std::string get_name(const VolumeGridData &grid)
std::string make_safe_name(const std::string &name, bool allow_unicode)
Definition usd_utils.cc:16
char is_sequence
ccl_device_inline int abs(int x)
Definition util/math.h:120