Blender V5.0
usd_asset_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 NVIDIA Corporation. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "usd_asset_utils.hh"
6#include "usd.hh"
7
8#include <pxr/usd/ar/asset.h>
9#include <pxr/usd/ar/packageUtils.h>
10#include <pxr/usd/ar/resolver.h>
11#include <pxr/usd/ar/writableAsset.h>
12#include <pxr/usd/usd/common.h>
13#include <pxr/usd/usd/stage.h>
14
15#include "BKE_appdir.hh"
16#include "BKE_idprop.hh"
17#include "BKE_main.hh"
18#include "BKE_report.hh"
19
20#include "BLI_fileops.hh"
21#include "BLI_path_utils.hh"
22#include "BLI_string.h"
23#include "BLI_string_utils.hh"
24
25#include "WM_api.hh"
26
27#include <string_view>
28
29namespace blender::io::usd {
30
31constexpr char UDIM_PATTERN[] = "<UDIM>";
32constexpr char UDIM_PATTERN2[] = "%3CUDIM%3E";
33
34/* Maximum range of UDIM tiles, per the
35 * UsdPreviewSurface specifications. See
36 * https://graphics.pixar.com/usd/release/spec_usdpreviewsurface.html#texture-reader
37 */
38constexpr int UDIM_START_TILE = 1001;
39constexpr int UDIM_END_TILE = 1100;
40
47static std::pair<std::string, std::string> split_udim_pattern(const std::string &path)
48{
49 std::string_view patterns[]{UDIM_PATTERN, UDIM_PATTERN2};
50 for (const std::string_view pattern : patterns) {
51 const std::string::size_type pos = path.find(pattern);
52 if (pos != std::string::npos) {
53 return {path.substr(0, pos), path.substr(pos + pattern.size())};
54 }
55 }
56
57 return {};
58}
59
60/* Return the asset file base name, with special handling of
61 * package relative paths. */
62static std::string get_asset_base_name(const std::string &src_path, ReportList *reports)
63{
64 char base_name[FILE_MAXFILE];
65
66 if (pxr::ArIsPackageRelativePath(src_path)) {
67 std::pair<std::string, std::string> split = pxr::ArSplitPackageRelativePathInner(src_path);
68 if (split.second.empty()) {
69 BKE_reportf(reports,
71 "%s: Couldn't determine package-relative file name from path %s",
72 __func__,
73 src_path.c_str());
74 return src_path;
75 }
76 BLI_path_split_file_part(split.second.c_str(), base_name, sizeof(base_name));
77 }
78 else {
79 BLI_path_split_file_part(src_path.c_str(), base_name, sizeof(base_name));
80 }
81
82 return base_name;
83}
84
85/* Copy an asset to a destination directory. */
86static std::string copy_asset_to_directory(const std::string &src_path,
87 const char *dest_dir_path,
88 eUSDTexNameCollisionMode name_collision_mode,
89 ReportList *reports)
90{
91 std::string base_name = get_asset_base_name(src_path, reports);
92
93 char dest_file_path[FILE_MAX];
94 BLI_path_join(dest_file_path, sizeof(dest_file_path), dest_dir_path, base_name.c_str());
95 BLI_path_normalize(dest_file_path);
96
97 if (name_collision_mode == USD_TEX_NAME_COLLISION_USE_EXISTING && BLI_is_file(dest_file_path)) {
98 return dest_file_path;
99 }
100
101 if (!copy_asset(src_path, dest_file_path, name_collision_mode, reports)) {
102 BKE_reportf(reports,
104 "%s: Couldn't copy file %s to %s",
105 __func__,
106 src_path.c_str(),
107 dest_file_path);
108 return src_path;
109 }
110
111 return dest_file_path;
112}
113
114static std::string copy_udim_asset_to_directory(const std::string &src_path,
115 const char *dest_dir_path,
116 eUSDTexNameCollisionMode name_collision_mode,
117 ReportList *reports)
118{
119 /* Get prefix and suffix from udim pattern. */
120 std::pair<std::string, std::string> splitPath = split_udim_pattern(src_path);
121 if (splitPath.first.empty() || splitPath.second.empty()) {
123 reports, RPT_ERROR, "%s: Couldn't split UDIM pattern %s", __func__, src_path.c_str());
124 return src_path;
125 }
126
127 /* Copy the individual UDIM tiles. Since there is currently no way to query the contents
128 * of a directory using the USD resolver, we must take a brute force approach. We iterate
129 * over the allowed range of tile indices and copy any tiles that exist. The USDPreviewSurface
130 * specification stipulates "a maximum of ten tiles in the U direction" and that
131 * "the tiles must be within the range [1001, 1100] (as of specification version 2.5)". See
132 * https://graphics.pixar.com/usd/release/spec_usdpreviewsurface.html#texture-reader
133 */
134 for (int i = UDIM_START_TILE; i <= UDIM_END_TILE; ++i) {
135 const std::string src_udim = splitPath.first + std::to_string(i) + splitPath.second;
136 if (asset_exists(src_udim)) {
137 copy_asset_to_directory(src_udim, dest_dir_path, name_collision_mode, reports);
138 }
139 }
140
141 const std::string src_file_name = get_asset_base_name(src_path, reports);
142 char ret_udim_path[FILE_MAX];
143 BLI_path_join(ret_udim_path, sizeof(ret_udim_path), dest_dir_path, src_file_name.c_str());
144
145 /* Blender only recognizes the <UDIM> pattern, not the
146 * alternative UDIM_PATTERN2, so we make sure the returned
147 * path has the former. */
148 splitPath = split_udim_pattern(ret_udim_path);
149 if (splitPath.first.empty() || splitPath.second.empty()) {
150 BKE_reportf(reports, RPT_ERROR, "%s: Couldn't split UDIM pattern %s", __func__, ret_udim_path);
151 return ret_udim_path;
152 }
153
154 return splitPath.first + UDIM_PATTERN + splitPath.second;
155}
156
157bool copy_asset(const std::string &src,
158 const std::string &dst,
159 eUSDTexNameCollisionMode name_collision_mode,
160 ReportList *reports)
161{
162 const pxr::ArResolver &ar = pxr::ArGetResolver();
163
164 if (name_collision_mode != USD_TEX_NAME_COLLISION_OVERWRITE) {
165 if (!ar.Resolve(dst).IsEmpty()) {
166 /* The asset exists, so this is a no-op. */
168 reports, RPT_INFO, "%s: Will not overwrite existing asset %s", __func__, dst.c_str());
169 return true;
170 }
171 }
172
173 pxr::ArResolvedPath src_path = ar.Resolve(src);
174
175 if (src_path.IsEmpty()) {
176 BKE_reportf(reports, RPT_ERROR, "%s: Cannot resolve path %s", __func__, src.c_str());
177 return false;
178 }
179
180 pxr::ArResolvedPath dst_path = ar.ResolveForNewAsset(dst);
181
182 if (dst_path.IsEmpty()) {
184 reports, RPT_ERROR, "%s: Cannot resolve path %s for writing", __func__, dst.c_str());
185 return false;
186 }
187
188 if (src_path == dst_path) {
189 BKE_reportf(reports,
190 RPT_ERROR,
191 "%s: Cannot copy %s. The source and destination paths are the same",
192 __func__,
193 src_path.GetPathString().c_str());
194 return false;
195 }
196
197 std::string why_not;
198 if (!ar.CanWriteAssetToPath(dst_path, &why_not)) {
199 BKE_reportf(reports,
200 RPT_ERROR,
201 "%s: Cannot write to asset %s: %s",
202 __func__,
203 dst_path.GetPathString().c_str(),
204 why_not.c_str());
205 return false;
206 }
207
208 std::shared_ptr<pxr::ArAsset> src_asset = ar.OpenAsset(src_path);
209 if (!src_asset) {
210 BKE_reportf(reports,
211 RPT_ERROR,
212 "%s: Cannot open source asset %s",
213 __func__,
214 src_path.GetPathString().c_str());
215 return false;
216 }
217
218 const size_t size = src_asset->GetSize();
219
220 if (size == 0) {
221 BKE_reportf(reports,
223 "%s: Will not copy zero size source asset %s",
224 __func__,
225 src_path.GetPathString().c_str());
226 return false;
227 }
228
229 std::shared_ptr<const char> buf = src_asset->GetBuffer();
230
231 if (!buf) {
232 BKE_reportf(reports,
233 RPT_ERROR,
234 "%s: Null buffer for source asset %s",
235 __func__,
236 src_path.GetPathString().c_str());
237 return false;
238 }
239
240 std::shared_ptr<pxr::ArWritableAsset> dst_asset = ar.OpenAssetForWrite(
241 dst_path, pxr::ArResolver::WriteMode::Replace);
242 if (!dst_asset) {
243 BKE_reportf(reports,
244 RPT_ERROR,
245 "%s: Cannot open destination asset %s for writing",
246 __func__,
247 src_path.GetPathString().c_str());
248 return false;
249 }
250
251 size_t bytes_written = dst_asset->Write(src_asset->GetBuffer().get(), src_asset->GetSize(), 0);
252
253 if (bytes_written == 0) {
254 BKE_reportf(reports,
255 RPT_ERROR,
256 "%s: Error writing to destination asset %s",
257 __func__,
258 dst_path.GetPathString().c_str());
259 }
260
261 if (!dst_asset->Close()) {
262 BKE_reportf(reports,
263 RPT_ERROR,
264 "%s: Couldn't close destination asset %s",
265 __func__,
266 dst_path.GetPathString().c_str());
267 return false;
268 }
269
270 return bytes_written > 0;
271}
272
273bool asset_exists(const std::string &path)
274{
275 return !pxr::ArGetResolver().Resolve(path).IsEmpty();
276}
277
278std::string import_asset(const std::string &src,
279 const char *import_dir,
280 eUSDTexNameCollisionMode name_collision_mode,
281 ReportList *reports)
282{
283 if (import_dir[0] == '\0') {
284 BKE_reportf(reports,
285 RPT_ERROR,
286 "%s: Texture import directory path empty, couldn't import %s",
287 __func__,
288 src.c_str());
289 return src;
290 }
291
292 char dest_dir_path[FILE_MAXDIR];
293 STRNCPY(dest_dir_path, import_dir);
294
295 if (BLI_path_is_rel(import_dir)) {
296 const char *basepath = BKE_main_blendfile_path_from_global();
297 if (basepath[0] == '\0') {
298 BKE_reportf(reports,
299 RPT_ERROR,
300 "%s: import directory is relative "
301 "but the blend file path is empty. "
302 "Please save the blend file before importing the USD "
303 "or provide an absolute import directory path. "
304 "Cannot import %s",
305 __func__,
306 src.c_str());
307 return src;
308 }
309 char path_temp[FILE_MAX];
310 STRNCPY(path_temp, dest_dir_path);
311 BLI_path_abs(path_temp, basepath);
312 STRNCPY(dest_dir_path, path_temp);
313 }
314
315 BLI_path_normalize(dest_dir_path);
316
317 if (!BLI_dir_create_recursive(dest_dir_path)) {
318 BKE_reportf(reports,
319 RPT_ERROR,
320 "%s: Couldn't create texture import directory %s",
321 __func__,
322 dest_dir_path);
323 return src;
324 }
325
326 if (is_udim_path(src)) {
327 return copy_udim_asset_to_directory(src, dest_dir_path, name_collision_mode, reports);
328 }
329
330 return copy_asset_to_directory(src, dest_dir_path, name_collision_mode, reports);
331}
332
340static bool parent_dir_exists_on_file_system(const std::string &path)
341{
342 char dir_path[FILE_MAX];
343 BLI_path_split_dir_part(path.c_str(), dir_path, FILE_MAX);
344 return BLI_is_dir(dir_path);
345}
346
347bool is_udim_path(const std::string &path)
348{
349 return path.find(UDIM_PATTERN) != std::string::npos ||
350 path.find(UDIM_PATTERN2) != std::string::npos;
351}
352
353std::string get_export_textures_dir(const pxr::UsdStageRefPtr stage)
354{
355 pxr::SdfLayerHandle layer = stage->GetRootLayer();
356
357 if (layer->IsAnonymous()) {
359 "%s: Cannot generate a textures directory path for anonymous stage",
360 __func__);
361 return "";
362 }
363
364 const pxr::ArResolvedPath &stage_path = layer->GetResolvedPath();
365
366 if (stage_path.empty()) {
367 WM_global_reportf(RPT_WARNING, "%s: Cannot get resolved path for stage", __func__);
368 return "";
369 }
370
371 const pxr::ArResolver &ar = pxr::ArGetResolver();
372
373 /* Resolve the `./textures` relative path, with the stage path as an anchor. */
374 std::string textures_dir = ar.CreateIdentifierForNewAsset("./textures", stage_path);
375
376 /* If parent of the stage path exists as a file system directory, try to create the
377 * textures directory. */
378 if (parent_dir_exists_on_file_system(stage_path.GetPathString())) {
379 BLI_dir_create_recursive(textures_dir.c_str());
380 }
381
382 return textures_dir;
383}
384
385bool should_import_asset(const std::string &path)
386{
387 if (path.empty()) {
388 return false;
389 }
390
391 if (BLI_path_is_rel(path.c_str())) {
392 return false;
393 }
394
395 if (pxr::ArIsPackageRelativePath(path)) {
396 return true;
397 }
398
400 return false;
401 }
402
403 return !BLI_is_file(path.c_str()) && asset_exists(path);
404}
405
406bool paths_equal(const std::string &path1, const std::string &path2)
407{
408 BLI_assert_msg(!BLI_path_is_rel(path1.c_str()) && !BLI_path_is_rel(path2.c_str()),
409 "Paths arguments must be absolute");
410
411 const pxr::ArResolver &ar = pxr::ArGetResolver();
412 return ar.ResolveForNewAsset(path1) == ar.ResolveForNewAsset(path2);
413}
414
415const char *temp_textures_dir()
416{
417 static bool inited = false;
418
419 static char temp_dir[FILE_MAXDIR] = {'\0'};
420
421 if (!inited) {
422 BLI_path_join(temp_dir, sizeof(temp_dir), BKE_tempdir_session(), "usd_textures_tmp", SEP_STR);
423 inited = true;
424 }
425
426 return temp_dir;
427}
428
429bool write_to_path(const void *data, size_t size, const std::string &path, ReportList *reports)
430{
432 if (size == 0) {
433 return false;
434 }
435
436 const pxr::ArResolver &ar = pxr::ArGetResolver();
437 pxr::ArResolvedPath resolved_path = ar.ResolveForNewAsset(path);
438
439 if (resolved_path.IsEmpty()) {
440 BKE_reportf(reports, RPT_ERROR, "Cannot resolve path %s for writing", path.c_str());
441 return false;
442 }
443
444 std::string why_not;
445 if (!ar.CanWriteAssetToPath(resolved_path, &why_not)) {
446 BKE_reportf(reports,
447 RPT_ERROR,
448 "Cannot write to asset %s: %s",
449 resolved_path.GetPathString().c_str(),
450 why_not.c_str());
451 return false;
452 }
453
454 std::shared_ptr<pxr::ArWritableAsset> dst_asset = ar.OpenAssetForWrite(
455 resolved_path, pxr::ArResolver::WriteMode::Replace);
456 if (!dst_asset) {
457 BKE_reportf(reports,
458 RPT_ERROR,
459 "Cannot open destination asset %s for writing",
460 resolved_path.GetPathString().c_str());
461 return false;
462 }
463
464 size_t bytes_written = dst_asset->Write(data, size, 0);
465
466 if (bytes_written == 0) {
467 BKE_reportf(reports,
468 RPT_ERROR,
469 "Error writing to destination asset %s",
470 resolved_path.GetPathString().c_str());
471 }
472
473 if (!dst_asset->Close()) {
474 BKE_reportf(reports,
475 RPT_ERROR,
476 "Couldn't close destination asset %s",
477 resolved_path.GetPathString().c_str());
478 return false;
479 }
480
481 return bytes_written > 0;
482}
483
484void ensure_usd_source_path_prop(const std::string &path, ID *id)
485{
486 if (!id || path.empty()) {
487 return;
488 }
489
490 if (pxr::ArIsPackageRelativePath(path)) {
491 /* Don't record package-relative paths (e.g., images in USDZ
492 * archives). */
493 return;
494 }
495
496 IDProperty *idgroup = IDP_EnsureProperties(id);
497
498 if (!idgroup) {
499 return;
500 }
501
502 const StringRef prop_name = "usd_source_path";
503
504 if (IDP_GetPropertyFromGroup(idgroup, prop_name)) {
505 return;
506 }
507
508 IDPropertyTemplate val = {0};
509 val.string.str = path.c_str();
510 /* Note length includes null terminator. */
511 val.string.len = path.size() + 1;
513
514 IDProperty *prop = IDP_New(IDP_STRING, &val, prop_name);
515
516 IDP_AddToGroup(idgroup, prop);
517}
518
519std::string get_usd_source_path(ID *id)
520{
521 if (!id) {
522 return "";
523 }
524
525 const IDProperty *idgroup = IDP_EnsureProperties(id);
526 if (!idgroup) {
527 return "";
528 }
529
530 const StringRef prop_name = "usd_source_path";
531 const IDProperty *prop = IDP_GetPropertyFromGroup(idgroup, prop_name);
532 if (!prop) {
533 return "";
534 }
535
536 return static_cast<const char *>(prop->data.pointer);
537}
538
539std::string get_relative_path(const std::string &path, const std::string &anchor)
540{
541 if (path.empty() || anchor.empty()) {
542 return path;
543 }
544
545 if (path == anchor) {
546 return path;
547 }
548
549 if (BLI_path_is_rel(path.c_str())) {
550 return path;
551 }
552
553 if (pxr::ArIsPackageRelativePath(path)) {
554 return path;
555 }
556
557 if (BLI_is_file(path.c_str()) && BLI_is_file(anchor.c_str())) {
558 /* Treat the paths as standard files. */
559 char rel_path[FILE_MAX];
560 STRNCPY(rel_path, path.c_str());
561 BLI_path_rel(rel_path, anchor.c_str());
562 if (!BLI_path_is_rel(rel_path)) {
563 return path;
564 }
565 BLI_string_replace_char(rel_path, '\\', '/');
566 return rel_path + 2;
567 }
568
569 /* If we got here, the paths may be URIs or files on the file system. */
570
571 /* We don't have a library to compute relative paths for URIs
572 * so we use the standard file-system calls to do so. This
573 * may not work for all URIs in theory, but is probably sufficient
574 * for the subset of URIs we are likely to encounter in practice
575 * currently.
576 * TODO(makowalski): provide better utilities for this. */
577
578 const pxr::ArResolver &ar = pxr::ArGetResolver();
579
580 std::string resolved_path = ar.Resolve(path);
581 std::string resolved_anchor = ar.Resolve(anchor);
582
583 if (resolved_path.empty() || resolved_anchor.empty()) {
584 return path;
585 }
586
587 std::string prefix = pxr::TfStringGetCommonPrefix(path, anchor);
588 if (prefix.empty()) {
589 return path;
590 }
591
592 std::replace(prefix.begin(), prefix.end(), '\\', '/');
593
594 size_t last_slash_pos = prefix.find_last_of('/');
595 if (last_slash_pos == std::string::npos) {
596 /* Unexpected: The prefix doesn't contain a slash,
597 * so this was not an absolute path. */
598 return path;
599 }
600
601 /* Replace the common prefix up to the last slash with
602 * a fake root directory to allow computing the relative path
603 * excluding the URI. We omit the URI because it might not
604 * be handled correctly by the standard file-system path computations. */
605 resolved_path = "/root" + resolved_path.substr(last_slash_pos);
606 resolved_anchor = "/root" + resolved_anchor.substr(last_slash_pos);
607
608 char anchor_parent_dir[FILE_MAX];
609 BLI_path_split_dir_part(resolved_anchor.c_str(), anchor_parent_dir, FILE_MAX);
610
611 if (anchor_parent_dir[0] == '\0') {
612 return path;
613 }
614
615 char result_path[FILE_MAX];
616 STRNCPY(result_path, resolved_path.c_str());
617 BLI_path_rel(result_path, anchor_parent_dir);
618
619 if (BLI_path_is_rel(result_path)) {
620 /* Strip the Blender relative path marker, and set paths to Unix-style. */
621 BLI_string_replace_char(result_path, '\\', '/');
622 return std::string(result_path + 2);
623 }
624
625 return path;
626}
627
628void USD_path_abs(char *path, const char *basepath, bool for_import)
629{
630 if (!BLI_path_is_rel(path)) {
631 pxr::ArResolvedPath resolved_path = for_import ? pxr::ArGetResolver().Resolve(path) :
632 pxr::ArGetResolver().ResolveForNewAsset(path);
633
634 const std::string &path_str = resolved_path.GetPathString();
635
636 if (!path_str.empty()) {
637 if (path_str.length() < FILE_MAX) {
638 BLI_strncpy(path, path_str.c_str(), FILE_MAX);
639 return;
640 }
642 "In %s: resolved path %s exceeds path buffer length.",
643 __func__,
644 path_str.c_str());
645 }
646 }
647
648 /* If we got here, the path couldn't be resolved by the ArResolver, so we
649 * fall back on the standard Blender absolute path resolution. */
650 BLI_path_abs(path, basepath);
651}
652
653} // namespace blender::io::usd
IDProperty * IDP_GetPropertyFromGroup(const IDProperty *prop, blender::StringRef name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:747
IDProperty * IDP_New(char type, const IDPropertyTemplate *val, blender::StringRef name, eIDPropertyFlag flags={}) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:1009
bool IDP_AddToGroup(IDProperty *group, IDProperty *prop) ATTR_NONNULL()
Definition idprop.cc:717
IDProperty * IDP_EnsureProperties(ID *id) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition idprop.cc:882
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:892
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
bool BLI_dir_create_recursive(const char *dirname) ATTR_NONNULL()
Definition fileops_c.cc:414
bool BLI_is_file(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:448
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:443
File and directory operations.
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAXFILE
#define FILE_MAX
int BLI_path_normalize(char *path) ATTR_NONNULL(1)
#define BLI_path_join(...)
void void void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy) ATTR_NONNULL(1
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
#define FILE_MAXDIR
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
void BLI_string_replace_char(char *str, char src, char dst) ATTR_NONNULL(1)
@ IDP_STRING_SUB_UTF8
@ IDP_STRING
static void split(const char *text, const char *seps, char ***str, int *count)
BMesh const char void * data
static const SubDPattern * patterns[]
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
uint pos
static std::string copy_asset_to_directory(const std::string &src_path, const char *dest_dir_path, eUSDTexNameCollisionMode name_collision_mode, ReportList *reports)
const char * temp_textures_dir()
bool should_import_asset(const std::string &path)
bool asset_exists(const std::string &path)
bool is_udim_path(const std::string &path)
std::string get_relative_path(const std::string &path, const std::string &anchor)
static std::string get_asset_base_name(const std::string &src_path, ReportList *reports)
bool write_to_path(const void *data, size_t size, const std::string &path, ReportList *reports)
bool paths_equal(const std::string &path1, const std::string &path2)
constexpr int UDIM_START_TILE
bool copy_asset(const std::string &src, const std::string &dst, eUSDTexNameCollisionMode name_collision_mode, ReportList *reports)
eUSDTexNameCollisionMode
Definition usd.hh:74
@ USD_TEX_NAME_COLLISION_USE_EXISTING
Definition usd.hh:75
@ USD_TEX_NAME_COLLISION_OVERWRITE
Definition usd.hh:76
constexpr char UDIM_PATTERN[]
void USD_path_abs(char *path, const char *basepath, bool for_import)
std::string get_export_textures_dir(const pxr::UsdStageRefPtr stage)
static std::pair< std::string, std::string > split_udim_pattern(const std::string &path)
constexpr int UDIM_END_TILE
std::string get_usd_source_path(ID *id)
void ensure_usd_source_path_prop(const std::string &path, ID *id)
constexpr char UDIM_PATTERN2[]
static bool parent_dir_exists_on_file_system(const std::string &path)
std::string import_asset(const std::string &src, const char *import_dir, eUSDTexNameCollisionMode name_collision_mode, ReportList *reports)
static std::string copy_udim_asset_to_directory(const std::string &src_path, const char *dest_dir_path, eUSDTexNameCollisionMode name_collision_mode, ReportList *reports)
void * pointer
Definition DNA_ID.h:142
IDPropertyData data
Definition DNA_ID.h:169
Definition DNA_ID.h:414
void * BKE_tempdir_session
Definition stubs.c:38
i
Definition text_draw.cc:230
const char * str
Definition BKE_idprop.hh:39
struct IDPropertyTemplate::@306303166102371126056157213146124155011254157272 string
#define SEP_STR
Definition unit.cc:39
void WM_global_reportf(eReportType type, const char *format,...)