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