diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 5968a6b7296..b81d2c90502 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -387,7 +387,17 @@ bool BKE_cachefile_filepath_get(const Main *bmain, char r_filepath[FILE_MAX]) { BLI_strncpy(r_filepath, cache_file->filepath, FILE_MAX); + +#ifdef WITH_USD + if (BLI_path_extension_check_glob(r_filepath, "*.usd;*.usda;*.usdc;*.usdz")) { + USD_path_abs(r_filepath, ID_BLEND_PATH(bmain, &cache_file->id), true /* for import */); + } + else { + BLI_path_abs(r_filepath, ID_BLEND_PATH(bmain, &cache_file->id)); + } +#else BLI_path_abs(r_filepath, ID_BLEND_PATH(bmain, &cache_file->id)); +#endif int fframe; int frame_len; diff --git a/source/blender/io/usd/intern/usd_asset_utils.cc b/source/blender/io/usd/intern/usd_asset_utils.cc index fe550c9d7ad..6a5ac4a81f1 100644 --- a/source/blender/io/usd/intern/usd_asset_utils.cc +++ b/source/blender/io/usd/intern/usd_asset_utils.cc @@ -8,6 +8,7 @@ #include #include +#include "BKE_appdir.h" #include "BKE_main.h" #include "BLI_fileops.h" @@ -300,4 +301,108 @@ bool is_udim_path(const std::string &path) path.find(UDIM_PATTERN2) != std::string::npos; } + std::string get_export_textures_dir(const pxr::UsdStageRefPtr stage) +{ + pxr::SdfLayerHandle layer = stage->GetRootLayer(); + + if (layer->IsAnonymous()) { + WM_reportf( + RPT_WARNING, "%s: Can't generate a textures directory path for anonymous stage", __func__); + return ""; + } + + pxr::ArResolvedPath stage_path = layer->GetResolvedPath(); + + if (stage_path.empty()) { + WM_reportf( + RPT_WARNING, "%s: Can't get resolved path for stage", __func__); + return ""; + } + + pxr::ArResolver &ar = pxr::ArGetResolver(); + + /* Resolove the './textures' relative path, with the stage path as an anchor. */ + std::string textures_dir = ar.CreateIdentifierForNewAsset("./textures", stage_path); + + /* If parent of the stage path exists as a file system directory, try to create the + * textures directory. */ + if (parent_dir_exists_on_file_system(stage_path.GetPathString().c_str())) { + BLI_dir_create_recursive(textures_dir.c_str()); + } + + return textures_dir; +} + +bool parent_dir_exists_on_file_system(const char *path) +{ + char dir_path[FILE_MAX]; + BLI_split_dir_part(path, dir_path, FILE_MAX); + return BLI_is_dir(dir_path); +} + +bool should_import_asset(const std::string &path) +{ + if (BLI_path_is_rel(path.c_str())) { + return false; + } + + if (pxr::ArIsPackageRelativePath(path)) { + return true; + } + + return !BLI_is_file(path.c_str()) && asset_exists(path.c_str()); +} + + bool paths_equal(const char *p1, const char *p2) + { + BLI_assert_msg(!BLI_path_is_rel(p1) && !BLI_path_is_rel(p2), + "Paths arguments must be absolute"); + + pxr::ArResolver &ar = pxr::ArGetResolver(); + + std::string resolved_p1 = ar.ResolveForNewAsset(p1).GetPathString(); + std::string resolved_p2 = ar.ResolveForNewAsset(p2).GetPathString(); + + return resolved_p1 == resolved_p2; + } + + const char *temp_textures_dir() + { + static bool inited = false; + + static char temp_dir[FILE_MAXDIR] = {'\0'}; + + if (!inited) { + BLI_path_join(temp_dir, sizeof(temp_dir), BKE_tempdir_session(), "usd_textures_tmp", SEP_STR); + inited = true; + } + + return temp_dir; + } + } // namespace blender::io::usd + + +void USD_path_abs(char *path, const char *basepath, bool for_import) +{ + if (!BLI_path_is_rel(path)) { + pxr::ArResolvedPath resolved_path = for_import ? pxr::ArGetResolver().Resolve(path) : + pxr::ArGetResolver().ResolveForNewAsset(path); + + std::string path_str = resolved_path.GetPathString(); + + if (!path_str.empty()) { + if (path_str.length() < FILE_MAX) { + BLI_strncpy(path, path_str.c_str(), FILE_MAX); + return; + } + WM_reportf(RPT_ERROR, + "In %s: resolved path %s exceeds path buffer length.", __func__, + path_str.c_str()); + } + } + + /* If we got here, the path couldn't be resolved by the ArResolver, so we + * fall back on the standard Blender absolute path resolution. */ + BLI_path_abs(path, basepath); +} diff --git a/source/blender/io/usd/intern/usd_asset_utils.h b/source/blender/io/usd/intern/usd_asset_utils.h index be4e95fba42..0dd660fa069 100644 --- a/source/blender/io/usd/intern/usd_asset_utils.h +++ b/source/blender/io/usd/intern/usd_asset_utils.h @@ -54,4 +54,64 @@ std::string import_asset(const char *src, */ bool is_udim_path(const std::string &path); +/** + * Invoke the USD asset resolver to return an identifier for a 'textures' directory + * which is a sibling of the given stage. The resulting path is created by + * resolving the './textures' relative path with the stage's root layer path as + * the anchor. If the parent of the stage root layer path resolves to a file + * system path, the textures directory will be created, if it doesn't exist. + * + * \param stage: The stage whose root layer is a sibling of the 'textures' + * directory + * \return the path to the 'textures' directory + */ +std::string get_export_textures_dir(const pxr::UsdStageRefPtr stage); + +/** + * Returns true if the parent directory of the given path exists on the + * file system. + * + * \param path: input file path + * \return true if the parent directory exists + */ +bool parent_dir_exists_on_file_system(const char *path); + +/** + * Return true if the asset at the given path is a candidate for importing + * with the USD asset resolver. The following heuristics are currently + * applied for this test: + * - Returns false if it's a Blender relative path. + * - Returns true if the path is package-relative. + * - Returns true is the path doesn't exist on the file system but can + * nonetheles be resolved by the USD asset resolver. + * - Returns false otherwise. + * + * TODO(makowalski): the test currently requires a file-system stat. + * Consider possible ways around this, e.g., by determining if the + * path is a supported URI. + * + * \param path: input file path + * \return true if the path should be imported, false otherwise + */ +bool should_import_asset(const std::string &path); + +/** + * Invokes the USD asset resolver to resolve the givn paths and + * returns true if the resolved paths are equal. + * + * \param p1: first path to compare + * \param p2: second path to compare + * \return true if the resolved input paths are equal, returns + * false otherwise. + * + */ +bool paths_equal(const char *p1, const char *p2); + + +/** + * Returns path to temporary folder for saving imported textures prior to packing. + * CAUTION: this directory is recursively deleted after material import. + */ +const char *temp_textures_dir(); + } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc index 17a5715ea46..4bbeb732708 100644 --- a/source/blender/io/usd/intern/usd_capi_export.cc +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -2,6 +2,7 @@ * Copyright 2019 Blender Foundation. All rights reserved. */ #include "usd.h" +#include "usd_asset_utils.h" #include "usd_common.h" #include "usd_hierarchy_iterator.h" #include "usd_light_convert.h" @@ -342,10 +343,9 @@ static bool perform_usdz_conversion(const ExportJobData *data) return false; } } - result = BLI_rename(usdz_temp_dirfile, data->usdz_filepath); - if (result != 0) { + if (!copy_asset(usdz_temp_dirfile, data->usdz_filepath, USD_TEX_NAME_COLLISION_OVERWRITE)) { WM_reportf(RPT_ERROR, - "USD Export: Couldn't move new usdz file from temporary location %s to %s", + "USD Export: Couldn't copy new usdz file from temporary location %s to %s", usdz_temp_dirfile, data->usdz_filepath); return false; diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index cd62aa127bb..eea85340f8b 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -386,7 +386,7 @@ static void import_startjob(void *customdata, bool *stop, bool *do_update, float data->view_layer, import_collection); } - BLI_path_abs(data->filepath, BKE_main_blendfile_path_from_global()); + USD_path_abs(data->filepath, BKE_main_blendfile_path_from_global(), true); *data->do_update = true; *data->progress = 0.05f; diff --git a/source/blender/io/usd/intern/usd_common.cc b/source/blender/io/usd/intern/usd_common.cc index 2cfd3c5196e..da623ced8ef 100644 --- a/source/blender/io/usd/intern/usd_common.cc +++ b/source/blender/io/usd/intern/usd_common.cc @@ -2,10 +2,17 @@ * Copyright 2021 Blender Foundation. All rights reserved. */ #include "usd_common.h" +#include "usd.h" +#include #include #include "BKE_appdir.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" +#include "WM_types.h" namespace blender::io::usd { diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc index 129d0105dd6..4d9118dca33 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -81,23 +81,6 @@ static const pxr::TfToken UsdPrimvarReader_float2("UsdPrimvarReader_float2", static const pxr::TfToken UsdUVTexture("UsdUVTexture", pxr::TfToken::Immortal); } // namespace usdtokens -/* Temporary folder for saving imported textures prior to packing. - * CAUTION: this directory is recursively deleted after material - * import. */ -static const char *temp_textures_dir() -{ - static bool inited = false; - - static char temp_dir[FILE_MAXDIR] = {'\0'}; - - if (!inited) { - BLI_path_join(temp_dir, sizeof(temp_dir), BKE_tempdir_session(), "usd_textures_tmp", SEP_STR); - inited = true; - } - - return temp_dir; -} - /* Add a node of the given type at the given location coordinates. */ static bNode *add_node( const bContext *C, bNodeTree *ntree, const int type, const float locx, const float locy) @@ -405,9 +388,14 @@ Material *USDMaterialReader::add_material(const pxr::UsdShadeMaterial &usd_mater } else if (params_.import_shaders_mode == USD_IMPORT_MDL) { bool has_mdl = false; + bool mdl_imported = false; #ifdef WITH_PYTHON /* Invoke UMM to convert to MDL. */ - bool mdl_imported = umm_import_mdl_material(mtl, usd_material, true /* Verbose */, &has_mdl); + mdl_imported = umm_import_mdl_material(params_, mtl, usd_material, true /* Verbose */, &has_mdl); + if (mdl_imported && params_.import_textures_mode == USD_TEX_IMPORT_PACK) { + /* Process the imported material to pack the textures. */ + pack_imported_textures(mtl); + } #endif if (!(has_mdl && mdl_imported) && usd_preview) { /* The material has no MDL shader or we couldn't convert the MDL, @@ -814,7 +802,7 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, /* Optionally copy the asset if it's inside a USDZ package. */ const bool import_textures = params_.import_textures_mode != USD_TEX_IMPORT_NONE && - pxr::ArIsPackageRelativePath(file_path); + should_import_asset(file_path); if (import_textures) { /* If we are packing the imported textures, we first write them @@ -927,6 +915,39 @@ void USDMaterialReader::convert_usd_primvar_reader_float2( link_nodes(ntree, uv_map, "UV", dest_node, dest_socket_name); } +void USDMaterialReader::pack_imported_textures(Material *material, bool delete_temp_textures_dir) const +{ + if (!(material && material->use_nodes)) { + return; + } + + for (bNode *node = (bNode *)material->nodetree->nodes.first; node; node = node->next) { + if (!(ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT))) { + continue; + } + Image *image = reinterpret_cast(node->id); + if (!image || BKE_image_has_packedfile(image)) { + continue; + } + + if (image->filepath[0] == '\0') { + continue; + } + + char dir_path[FILE_MAXDIR]; + BLI_split_dir_part(image->filepath, dir_path, sizeof(dir_path)); + + if (BLI_path_cmp_normalized(dir_path, temp_textures_dir()) == 0) { + /* Texture was saved to the temporary import directory, so pack it. */ + BKE_image_packfiles(nullptr, image, ID_BLEND_PATH(bmain_, &image->id)); + } + } + + if (delete_temp_textures_dir && BLI_is_dir(temp_textures_dir())) { + BLI_delete(temp_textures_dir(), true, true); + } +} + void build_material_map(const Main *bmain, std::map *r_mat_map) { BLI_assert_msg(r_mat_map, "..."); diff --git a/source/blender/io/usd/intern/usd_reader_material.h b/source/blender/io/usd/intern/usd_reader_material.h index 1928ad16519..903bba3adb3 100644 --- a/source/blender/io/usd/intern/usd_reader_material.h +++ b/source/blender/io/usd/intern/usd_reader_material.h @@ -127,6 +127,12 @@ class USDMaterialReader { bNodeTree *ntree, int column, NodePlacementContext *r_ctx) const; + + /** + * Pack imported textures referenced by this material and optionally delete + * the temporary textures import directory when done processing. + */ + void pack_imported_textures(Material *material, bool delete_temp_textures_dir=true) const; }; /* Utility functions. */ diff --git a/source/blender/io/usd/intern/usd_umm.cc b/source/blender/io/usd/intern/usd_umm.cc index c648c2f2c23..3fd3919809f 100644 --- a/source/blender/io/usd/intern/usd_umm.cc +++ b/source/blender/io/usd/intern/usd_umm.cc @@ -19,6 +19,7 @@ # include "usd_umm.h" # include "usd.h" +# include "usd_asset_utils.h" # include "usd_exporter_context.h" # include "usd_writer_material.h" @@ -380,7 +381,8 @@ static void test_python() PyGILState_Release(gilstate); } -static PyObject *get_shader_source_data(const pxr::UsdShadeShader &usd_shader) +static PyObject *get_shader_source_data(const USDImportParams ¶ms, + const pxr::UsdShadeShader &usd_shader) { if (!usd_shader) { return nullptr; @@ -463,6 +465,24 @@ static PyObject *get_shader_source_data(const pxr::UsdShadeShader &usd_shader) asset_path.GetAssetPath()); } + const bool import_textures = params.import_textures_mode != USD_TEX_IMPORT_NONE && + should_import_asset(resolved_path); + + if (import_textures) { + /* If we are packing the imported textures, we first write them + * to a temporary directory. */ + const char *textures_dir = params.import_textures_mode == USD_TEX_IMPORT_PACK ? + temp_textures_dir() : + params.import_textures_dir; + + const eUSDTexNameCollisionMode name_collision_mode = params.import_textures_mode == + USD_TEX_IMPORT_PACK ? + USD_TEX_NAME_COLLISION_OVERWRITE : + params.tex_name_collision_mode; + + resolved_path = import_asset(resolved_path.c_str(), textures_dir, name_collision_mode); + } + pxr::TfToken color_space_tok = usd_attr.GetColorSpace(); if (color_space_tok.IsEmpty() && have_connected_source) { @@ -527,7 +547,8 @@ static PyObject *get_shader_source_data(const pxr::UsdShadeShader &usd_shader) return ret; } -static bool import_material(Material *mtl, +static bool import_material(const USDImportParams ¶ms, + Material *mtl, const pxr::UsdShadeShader &usd_shader, const std::string &source_class) { @@ -558,7 +579,7 @@ static bool import_material(Material *mtl, return false; } - PyObject *source_data = get_shader_source_data(usd_shader); + PyObject *source_data = get_shader_source_data(params, usd_shader); if (!source_data) { std::cout << "WARNING: Couldn't get source data for shader " << usd_shader.GetPath() @@ -747,7 +768,8 @@ bool umm_module_loaded() return loaded; } -bool umm_import_mdl_material(Material *mtl, +bool umm_import_mdl_material(const USDImportParams ¶ms, + Material *mtl, const pxr::UsdShadeMaterial &usd_material, bool verbose, bool *r_has_mdl) @@ -796,7 +818,7 @@ bool umm_import_mdl_material(Material *mtl, } std::string source_class = path + "|" + source_asset_sub_identifier.GetString(); - return import_material(mtl, surf_shader, source_class); + return import_material(params, mtl, surf_shader, source_class); } return false; diff --git a/source/blender/io/usd/intern/usd_umm.h b/source/blender/io/usd/intern/usd_umm.h index 718f54f53ab..af1d5e7c1b4 100644 --- a/source/blender/io/usd/intern/usd_umm.h +++ b/source/blender/io/usd/intern/usd_umm.h @@ -24,6 +24,7 @@ # include "Python.h" struct Material; +struct USDImportParams; namespace blender::io::usd { @@ -31,7 +32,8 @@ struct USDExporterContext; bool umm_module_loaded(); -bool umm_import_mdl_material(Material *mtl, +bool umm_import_mdl_material(const USDImportParams ¶ms, + Material *mtl, const pxr::UsdShadeMaterial &usd_material, bool verbose, bool *r_has_material); diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc index 42dcbb286d9..ac66f3f0165 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.cc +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -238,13 +238,13 @@ pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(const HierarchyCont usd_material, this->usd_export_context_.export_params); if (this->usd_export_context_.export_params.export_textures) { - export_textures(material, this->usd_export_context_.stage); + export_textures(material, this->usd_export_context_.stage, this->usd_export_context_.export_params.overwrite_textures); } } if (material->use_nodes && this->usd_export_context_.export_params.generate_mdl) { create_mdl_material(this->usd_export_context_, material, usd_material); if (this->usd_export_context_.export_params.export_textures) { - export_textures(material, this->usd_export_context_.stage); + export_textures(material, this->usd_export_context_.stage, this->usd_export_context_.export_params.overwrite_textures); } } if (material->use_nodes && this->usd_export_context_.export_params.generate_preview_surface) { diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index 1c1e21b0439..67b92403bb8 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -3,9 +3,11 @@ #include "usd_writer_material.h" #include "usd.h" +#include "usd_asset_utils.h" #include "usd_exporter_context.h" #include "usd_umm.h" +#include "BKE_appdir.h" #include "BKE_colorband.h" #include "BKE_colortools.h" #include "BKE_curve.h" @@ -458,21 +460,55 @@ static void export_in_memory_texture(Image *ima, char export_path[FILE_MAX]; BLI_path_join(export_path, FILE_MAX, export_dir.c_str(), file_name); + BLI_str_replace_char(export_path, '\\', '/'); - if (!allow_overwrite && BLI_exists(export_path)) { + if (!allow_overwrite && asset_exists(export_path)) { return; } - if ((BLI_path_cmp_normalized(export_path, image_abs_path) == 0) && BLI_exists(image_abs_path)) { + if (paths_equal(export_path, image_abs_path) && asset_exists(image_abs_path)) { /* As a precaution, don't overwrite the original path. */ return; } std::cout << "Exporting in-memory texture to " << export_path << std::endl; - if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) { - WM_reportf(RPT_WARNING, "USD export: couldn't export in-memory texture to %s", export_path); + if (BLI_is_dir(export_dir.c_str())) { + /* We are copying to a file system directory, so we can write the image buffer + * directly to the destination. */ + if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) { + WM_reportf(RPT_WARNING, + "USD export: couldn't save in-memory texture to %s", export_path); + } + return; } + + /* If we got here, the export directory path is not on the file system, which + * would be the case if we the path is a URI. We therefore can't save the image + * directly (because BKE_imbuf_write_as() can't resolve URIs) and must save + * the image to a temporary location on disk before copyig it to its final + * destination. */ + + char temp_filepath[FILE_MAX]; + BLI_path_join(temp_filepath, FILE_MAX, BKE_tempdir_session(), file_name); + + std::cout << "Saving in-memory texture to temporary location " << temp_filepath << std::endl; + + if (BKE_imbuf_write_as(imbuf, temp_filepath, &imageFormat, true) == 0) { + WM_reportf(RPT_WARNING, "USD export: couldn't save in-memory texture to temporary location %s", temp_filepath); + } + + /* Copy to destination. */ + if (!copy_asset(temp_filepath, + export_path, + allow_overwrite ? USD_TEX_NAME_COLLISION_OVERWRITE : + USD_TEX_NAME_COLLISION_USE_EXISTING)) { + WM_reportf(RPT_WARNING, + "USD export: couldn't export in-memory texture to %s", + temp_filepath); + } + + BLI_delete(temp_filepath, false, false); } /* Get the absolute filepath of the given image. Assumes @@ -481,8 +517,7 @@ static void get_absolute_path(Image *ima, char *r_path) { /* Make absolute source path. */ BLI_strncpy(r_path, ima->filepath, FILE_MAX); - BLI_path_abs(r_path, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); - BLI_path_normalize(nullptr, r_path); + USD_path_abs(r_path, ID_BLEND_PATH_FROM_GLOBAL(&ima->id), false /* Not for import */); } /* ===== Functions copied from inacessible source file @@ -2347,6 +2382,10 @@ static void copy_tiled_textures(Image *ima, return; } + const eUSDTexNameCollisionMode tex_name_collision_mode = allow_overwrite ? + USD_TEX_NAME_COLLISION_OVERWRITE : + USD_TEX_NAME_COLLISION_USE_EXISTING; + /* Copy all tiles. */ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { char src_tile_path[FILE_MAX]; @@ -2358,21 +2397,21 @@ static void copy_tiled_textures(Image *ima, char dest_tile_path[FILE_MAX]; BLI_path_join(dest_tile_path, FILE_MAX, dest_dir.c_str(), dest_filename); + BLI_str_replace_char(dest_tile_path, '\\', '/'); - if (!allow_overwrite && BLI_exists(dest_tile_path)) { + if (!allow_overwrite && asset_exists(dest_tile_path)) { continue; } - if (BLI_path_cmp_normalized(src_tile_path, dest_tile_path) == 0) { + if (paths_equal(src_tile_path, dest_tile_path)) { /* Source and destination paths are the same, don't copy. */ continue; } - std::cout << "Copying texture tile from " << src_tile_path << " to " << dest_tile_path - << std::endl; - /* Copy the file. */ - if (BLI_copy(src_tile_path, dest_tile_path) != 0) { + if (!copy_asset(src_tile_path, + dest_tile_path, + tex_name_collision_mode)) { WM_reportf(RPT_WARNING, "USD export: couldn't copy texture tile from %s to %s", src_tile_path, @@ -2393,20 +2432,22 @@ static void copy_single_file(Image *ima, const std::string &dest_dir, const bool char dest_path[FILE_MAX]; BLI_path_join(dest_path, FILE_MAX, dest_dir.c_str(), file_name); + BLI_str_replace_char(dest_path, '\\', '/'); - if (!allow_overwrite && BLI_exists(dest_path)) { + if (!allow_overwrite && asset_exists(dest_path)) { return; } - if (BLI_path_cmp_normalized(source_path, dest_path) == 0) { + if (paths_equal(source_path, dest_path)) { /* Source and destination paths are the same, don't copy. */ return; } - std::cout << "Copying texture from " << source_path << " to " << dest_path << std::endl; - /* Copy the file. */ - if (BLI_copy(source_path, dest_path) != 0) { + if (!copy_asset(source_path, + dest_path, + allow_overwrite ? USD_TEX_NAME_COLLISION_OVERWRITE : + USD_TEX_NAME_COLLISION_USE_EXISTING)) { WM_reportf( RPT_WARNING, "USD export: couldn't copy texture from %s to %s", source_path, dest_path); } @@ -2428,22 +2469,13 @@ void export_texture(bNode *node, return; } - pxr::SdfLayerHandle layer = stage->GetRootLayer(); - std::string stage_path = layer->GetRealPath(); - if (stage_path.empty()) { + std::string dest_dir = get_export_textures_dir(stage); + + if (dest_dir.empty()) { + WM_reportf(RPT_WARNING, "%s: Couldn't determine textures directory path", __func__); return; } - char usd_dir_path[FILE_MAX]; - BLI_split_dir_part(stage_path.c_str(), usd_dir_path, FILE_MAX); - - char tex_dir_path[FILE_MAX]; - BLI_path_join(tex_dir_path, FILE_MAX, usd_dir_path, "textures", SEP_STR); - - BLI_dir_create_recursive(tex_dir_path); - - std::string dest_dir(tex_dir_path); - if (is_in_memory_texture(ima)) { export_in_memory_texture(ima, dest_dir, allow_overwrite); } @@ -2456,7 +2488,7 @@ void export_texture(bNode *node, } /* Export the texture of every texture image node in the given material's node tree. */ -void export_textures(const Material *material, const pxr::UsdStageRefPtr stage) +void export_textures(const Material *material, const pxr::UsdStageRefPtr stage, const bool allow_overwrite) { if (!(material && material->use_nodes)) { return; @@ -2467,8 +2499,8 @@ void export_textures(const Material *material, const pxr::UsdStageRefPtr stage) } for (bNode *node = (bNode *)material->nodetree->nodes.first; node; node = node->next) { - if (node->type == SH_NODE_TEX_IMAGE || SH_NODE_TEX_ENVIRONMENT) { - export_texture(node, stage); + if (ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT)) { + export_texture(node, stage, allow_overwrite); } } } diff --git a/source/blender/io/usd/intern/usd_writer_material.h b/source/blender/io/usd/intern/usd_writer_material.h index 5831a80fbef..1d601663e63 100644 --- a/source/blender/io/usd/intern/usd_writer_material.h +++ b/source/blender/io/usd/intern/usd_writer_material.h @@ -74,7 +74,9 @@ std::string get_tex_image_asset_path(const std::string &asset_path, const pxr::UsdStageRefPtr stage, const USDExportParams &export_params); -void export_textures(const Material *material, const pxr::UsdStageRefPtr stage); +void export_textures(const Material *material, + const pxr::UsdStageRefPtr stage, + bool allow_overwrite = false); } // namespace blender::io::usd diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 4e49de2cceb..05765fa66b7 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -234,6 +234,12 @@ bool USD_umm_module_loaded(void); /* USD Import and Mesh Cache interface. */ +/* Similar to BLI_path_abs(), but also invokes the USD asset resolver + * to determine the absolute path. This is necessary for resolving + * paths with URIs that BLI_path_abs() would otherwise alter when + * attempting to normalize the path. */ +void USD_path_abs(char *path, const char *basepath, bool for_import); + struct CacheArchiveHandle *USD_create_handle(struct Main *bmain, const char *filepath, struct ListBase *object_paths);