Blender  V3.3
usd_writer_volume.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "usd_writer_volume.h"
4 
5 #include <pxr/base/tf/pathUtils.h>
6 #include <pxr/usd/usdVol/openVDBAsset.h>
7 #include <pxr/usd/usdVol/volume.h>
8 
9 #include "DNA_volume_types.h"
11 
12 #include "BKE_volume.h"
13 
14 #include "BLI_fileops.h"
15 #include "BLI_index_range.hh"
16 #include "BLI_path_util.h"
17 #include "BLI_string.h"
18 
19 #include "WM_api.h"
20 
21 #include "usd_hierarchy_iterator.h"
22 
23 namespace blender::io::usd {
24 
26 {
27 }
28 
30 {
31  const Volume *volume = static_cast<Volume *>(context.object->data);
32  return volume->is_sequence;
33 }
34 
36 {
37  Volume *volume = static_cast<Volume *>(context.object->data);
39  return;
40  }
41 
42  const int num_grids = BKE_volume_num_grids(volume);
43  if (!num_grids) {
44  return;
45  }
46 
47  auto vdb_file_path = resolve_vdb_file(volume);
48  if (!vdb_file_path.has_value()) {
50  "USD Export: failed to resolve .vdb file for object: %s",
51  volume->id.name + 2);
52  return;
53  }
54 
56  if (auto relative_vdb_file_path = construct_vdb_relative_file_path(*vdb_file_path)) {
57  vdb_file_path = relative_vdb_file_path;
58  }
59  else {
61  "USD Export: couldn't construct relative file path for .vdb file, absolute path "
62  "will be used instead");
63  }
64  }
65 
66  const pxr::UsdTimeCode timecode = get_export_time_code();
67  const pxr::SdfPath &volume_path = usd_export_context_.usd_path;
68  pxr::UsdStageRefPtr stage = usd_export_context_.stage;
69  pxr::UsdVolVolume usd_volume = pxr::UsdVolVolume::Define(stage, volume_path);
70 
71  for (const int i : IndexRange(num_grids)) {
72  const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
73  const std::string grid_name = BKE_volume_grid_name(grid);
74  const std::string grid_id = pxr::TfMakeValidIdentifier(grid_name);
75  const pxr::SdfPath grid_path = volume_path.AppendPath(pxr::SdfPath(grid_id));
76  pxr::UsdVolOpenVDBAsset usd_grid = pxr::UsdVolOpenVDBAsset::Define(stage, grid_path);
77  usd_grid.GetFieldNameAttr().Set(pxr::TfToken(grid_name), timecode);
78  usd_grid.GetFilePathAttr().Set(pxr::SdfAssetPath(*vdb_file_path), timecode);
79  usd_volume.CreateFieldRelationship(pxr::TfToken(grid_id), grid_path);
80  }
81 
82  float3 volume_bound_min(std::numeric_limits<float>::max());
83  float3 volume_bound_max(std::numeric_limits<float>::min());
84  if (BKE_volume_min_max(volume, volume_bound_min, volume_bound_max)) {
85  const pxr::VtArray<pxr::GfVec3f> volume_extent = {pxr::GfVec3f(&volume_bound_min[0]),
86  pxr::GfVec3f(&volume_bound_max[0])};
87  usd_volume.GetExtentAttr().Set(volume_extent, timecode);
88  }
89 
90  BKE_volume_unload(volume);
91 }
92 
93 std::optional<std::string> USDVolumeWriter::resolve_vdb_file(const Volume *volume) const
94 {
95  std::optional<std::string> vdb_file_path;
96  if (volume->filepath[0] == '\0') {
97  /* Entering this section should mean that Volume object contains OpenVDB data that is not
98  * obtained from external .vdb file but rather generated inside of Blender (i.e. by 'Mesh to
99  * Volume' modifier). Try to save this data to a .vdb file. */
100 
101  vdb_file_path = construct_vdb_file_path(volume);
102  if (!BKE_volume_save(
103  volume, usd_export_context_.bmain, nullptr, vdb_file_path.value_or("").c_str())) {
104  return std::nullopt;
105  }
106  }
107 
108  if (!vdb_file_path.has_value()) {
109  vdb_file_path = BKE_volume_grids_frame_filepath(volume);
110  if (vdb_file_path->empty()) {
111  return std::nullopt;
112  }
113  }
114 
115  return vdb_file_path;
116 }
117 
118 std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume *volume) const
119 {
120  const std::string usd_file_path = get_export_file_path();
121  if (usd_file_path.empty()) {
122  return std::nullopt;
123  }
124 
125  char usd_directory_path[FILE_MAX];
126  char usd_file_name[FILE_MAXFILE];
127  BLI_split_dirfile(usd_file_path.c_str(),
128  usd_directory_path,
129  usd_file_name,
130  sizeof(usd_directory_path),
131  sizeof(usd_file_name));
132 
133  if (usd_directory_path[0] == '\0' || usd_file_name[0] == '\0') {
134  return std::nullopt;
135  }
136 
137  const char *vdb_directory_name = "volumes";
138 
139  char vdb_directory_path[FILE_MAX];
140  BLI_strncpy(vdb_directory_path, usd_directory_path, FILE_MAX);
141  strcat(vdb_directory_path, vdb_directory_name);
142  BLI_dir_create_recursive(vdb_directory_path);
143 
144  char vdb_file_name[FILE_MAXFILE];
145  BLI_strncpy(vdb_file_name, volume->id.name + 2, FILE_MAXFILE);
146  const pxr::UsdTimeCode timecode = get_export_time_code();
147  if (!timecode.IsDefault()) {
148  const int frame = (int)timecode.GetValue();
149  const int num_frame_digits = frame == 0 ? 1 : integer_digits_i(abs(frame));
150  BLI_path_frame(vdb_file_name, frame, num_frame_digits);
151  }
152  strcat(vdb_file_name, ".vdb");
153 
154  char vdb_file_path[FILE_MAX];
155  BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, NULL);
156 
157  return vdb_file_path;
158 }
159 
160 std::optional<std::string> USDVolumeWriter::construct_vdb_relative_file_path(
161  const std::string &vdb_file_path) const
162 {
163  const std::string usd_file_path = get_export_file_path();
164  if (usd_file_path.empty()) {
165  return std::nullopt;
166  }
167 
168  char relative_path[FILE_MAX];
169  BLI_strncpy(relative_path, vdb_file_path.c_str(), FILE_MAX);
170  BLI_path_rel(relative_path, usd_file_path.c_str());
171  if (!BLI_path_is_rel(relative_path)) {
172  return std::nullopt;
173  }
174 
175  /* Following code was written with an assumption that Blender's relative paths start with
176  * // characters as well as have OS dependent slashes. Inside of USD files those relative
177  * paths should start with either ./ or ../ characters and have always forward slashes (/)
178  * separating directories. This is the convention used in USD documentation (and it seems
179  * to be used in other DCC packages as well). */
180  std::string relative_path_processed = pxr::TfNormPath(relative_path + 2);
181  if (relative_path_processed[0] != '.') {
182  relative_path_processed.insert(0, "./");
183  }
184 
185  return relative_path_processed;
186 }
187 
188 } // namespace blender::io::usd
Volume data-block.
const VolumeGrid * BKE_volume_grid_get_for_read(const struct Volume *volume, int grid_index)
void BKE_volume_unload(struct Volume *volume)
Definition: volume.cc:930
bool BKE_volume_save(const struct Volume *volume, const struct Main *bmain, struct ReportList *reports, const char *filepath)
const char * BKE_volume_grids_frame_filepath(const struct Volume *volume)
const char * BKE_volume_grid_name(const struct VolumeGrid *grid)
int BKE_volume_num_grids(const struct Volume *volume)
bool BKE_volume_load(const struct Volume *volume, const struct Main *bmain)
File and directory operations.
bool BLI_dir_create_recursive(const char *dir) ATTR_NONNULL()
Definition: fileops.c:1219
MINLINE int integer_digits_i(int i)
bool BLI_path_is_rel(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:347
bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL()
Definition: path_util.c:709
#define FILE_MAXFILE
void BLI_split_dirfile(const char *string, char *dir, char *file, size_t dirlen, size_t filelen)
Definition: path_util.c:1465
#define FILE_MAX
size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path_first,...) ATTR_NONNULL(1
void BLI_path_rel(char *file, const char *relfile) ATTR_NONNULL()
Definition: path_util.c:450
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
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:89
T abs(const T &a)
#define min(a, b)
Definition: sort.c:35
char name[66]
Definition: DNA_ID.h:378
bool relative_paths
Definition: usd.h:38
char filepath[1024]
char is_sequence
float max
bool BKE_volume_min_max(const Volume *volume, float3 &r_min, float3 &r_max)
Definition: volume.cc:981
void WM_reportf(eReportType type, const char *format,...)