diff --git a/source/blender/editors/io/io_usd.cc b/source/blender/editors/io/io_usd.cc index e8810b5e7d4..91427d84104 100644 --- a/source/blender/editors/io/io_usd.cc +++ b/source/blender/editors/io/io_usd.cc @@ -179,7 +179,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) STRNCPY(params.root_prim_path, root_prim_path); - bool ok = USD_export(C, filepath, ¶ms, as_background_job); + bool ok = USD_export(C, filepath, ¶ms, as_background_job, op->reports); return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } @@ -515,7 +515,7 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) STRNCPY(params.import_textures_dir, import_textures_dir); - const bool ok = USD_import(C, filepath, ¶ms, as_background_job); + const bool ok = USD_import(C, filepath, ¶ms, as_background_job, op->reports); return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } diff --git a/source/blender/io/usd/hydra/usd_scene_delegate.cc b/source/blender/io/usd/hydra/usd_scene_delegate.cc index 90acbd271a6..c069626a6f9 100644 --- a/source/blender/io/usd/hydra/usd_scene_delegate.cc +++ b/source/blender/io/usd/hydra/usd_scene_delegate.cc @@ -9,9 +9,13 @@ #include "BLI_string.h" #include "BKE_appdir.h" +#include "BKE_report.h" #include "DEG_depsgraph_query.hh" +#include "WM_api.hh" +#include "WM_types.hh" + #include "usd.h" #include "usd.hh" @@ -50,6 +54,14 @@ void USDSceneDelegate::populate(Depsgraph *depsgraph) params.export_textures = false; /* Don't copy all textures, is slow. */ params.evaluation_mode = DEG_get_mode(depsgraph); + /* NOTE: Since the reports list will be `nullptr` here, reports generated by export code from + * this call will only be printed to console. */ + wmJobWorkerStatus worker_status = {}; + ReportList worker_reports = {}; + BKE_reports_init(&worker_reports, RPT_PRINT | RPT_STORE); + worker_status.reports = &worker_reports; + params.worker_status = &worker_status; + /* Create clean directory for export. */ BLI_delete(temp_dir_.c_str(), true, true); BLI_dir_create_recursive(temp_dir_.c_str()); @@ -62,6 +74,8 @@ void USDSceneDelegate::populate(Depsgraph *depsgraph) stage_ = io::usd::export_to_stage(params, depsgraph, temp_file_.c_str()); delegate_ = std::make_unique(render_index_, delegate_id_); delegate_->Populate(stage_->GetPseudoRoot()); + + WM_reports_from_reports_move(nullptr, &worker_reports); } } // namespace blender::io::hydra diff --git a/source/blender/io/usd/intern/usd_asset_utils.cc b/source/blender/io/usd/intern/usd_asset_utils.cc index db547e9385f..5e9a79db6ff 100644 --- a/source/blender/io/usd/intern/usd_asset_utils.cc +++ b/source/blender/io/usd/intern/usd_asset_utils.cc @@ -10,6 +10,7 @@ #include #include "BKE_main.h" +#include "BKE_report.h" #include "BLI_fileops.h" #include "BLI_path_util.h" @@ -52,17 +53,18 @@ static std::pair split_udim_pattern(const std::string /* Return the asset file base name, with special handling of * package relative paths. */ -static std::string get_asset_base_name(const char *src_path) +static std::string get_asset_base_name(const char *src_path, ReportList *reports) { char base_name[FILE_MAXFILE]; if (pxr::ArIsPackageRelativePath(src_path)) { std::pair split = pxr::ArSplitPackageRelativePathInner(src_path); if (split.second.empty()) { - WM_reportf(RPT_WARNING, - "%s: Couldn't determine package-relative file name from path %s", - __func__, - src_path); + BKE_reportf(reports, + RPT_WARNING, + "%s: Couldn't determine package-relative file name from path %s", + __func__, + src_path); return src_path; } BLI_path_split_file_part(split.second.c_str(), base_name, sizeof(base_name)); @@ -77,9 +79,10 @@ static std::string get_asset_base_name(const char *src_path) /* Copy an asset to a destination directory. */ static std::string copy_asset_to_directory(const char *src_path, const char *dest_dir_path, - eUSDTexNameCollisionMode name_collision_mode) + eUSDTexNameCollisionMode name_collision_mode, + ReportList *reports) { - std::string base_name = get_asset_base_name(src_path); + std::string base_name = get_asset_base_name(src_path, reports); char dest_file_path[FILE_MAX]; BLI_path_join(dest_file_path, sizeof(dest_file_path), dest_dir_path, base_name.c_str()); @@ -89,8 +92,13 @@ static std::string copy_asset_to_directory(const char *src_path, return dest_file_path; } - if (!copy_asset(src_path, dest_file_path, name_collision_mode)) { - WM_reportf(RPT_WARNING, "%s: Couldn't copy file %s to %s", __func__, src_path, dest_file_path); + if (!copy_asset(src_path, dest_file_path, name_collision_mode, reports)) { + BKE_reportf(reports, + RPT_WARNING, + "%s: Couldn't copy file %s to %s", + __func__, + src_path, + dest_file_path); return src_path; } @@ -99,12 +107,13 @@ static std::string copy_asset_to_directory(const char *src_path, static std::string copy_udim_asset_to_directory(const char *src_path, const char *dest_dir_path, - eUSDTexNameCollisionMode name_collision_mode) + eUSDTexNameCollisionMode name_collision_mode, + ReportList *reports) { /* Get prefix and suffix from udim pattern. */ std::pair splitPath = split_udim_pattern(src_path); if (splitPath.first.empty() || splitPath.second.empty()) { - WM_reportf(RPT_ERROR, "%s: Couldn't split UDIM pattern %s", __func__, src_path); + BKE_reportf(reports, RPT_ERROR, "%s: Couldn't split UDIM pattern %s", __func__, src_path); return src_path; } @@ -118,11 +127,11 @@ static std::string copy_udim_asset_to_directory(const char *src_path, for (int i = UDIM_START_TILE; i < UDIM_END_TILE; ++i) { const std::string src_udim = splitPath.first + std::to_string(i) + splitPath.second; if (asset_exists(src_udim.c_str())) { - copy_asset_to_directory(src_udim.c_str(), dest_dir_path, name_collision_mode); + copy_asset_to_directory(src_udim.c_str(), dest_dir_path, name_collision_mode, reports); } } - const std::string src_file_name = get_asset_base_name(src_path); + const std::string src_file_name = get_asset_base_name(src_path, reports); char ret_udim_path[FILE_MAX]; BLI_path_join(ret_udim_path, sizeof(ret_udim_path), dest_dir_path, src_file_name.c_str()); @@ -131,14 +140,17 @@ static std::string copy_udim_asset_to_directory(const char *src_path, * path has the former. */ splitPath = split_udim_pattern(ret_udim_path); if (splitPath.first.empty() || splitPath.second.empty()) { - WM_reportf(RPT_ERROR, "%s: Couldn't split UDIM pattern %s", __func__, ret_udim_path); + BKE_reportf(reports, RPT_ERROR, "%s: Couldn't split UDIM pattern %s", __func__, ret_udim_path); return ret_udim_path; } return splitPath.first + UDIM_PATTERN + splitPath.second; } -bool copy_asset(const char *src, const char *dst, eUSDTexNameCollisionMode name_collision_mode) +bool copy_asset(const char *src, + const char *dst, + eUSDTexNameCollisionMode name_collision_mode, + ReportList *reports) { if (!(src && dst)) { return false; @@ -149,7 +161,7 @@ bool copy_asset(const char *src, const char *dst, eUSDTexNameCollisionMode name_ if (name_collision_mode != USD_TEX_NAME_COLLISION_OVERWRITE) { if (!ar.Resolve(dst).IsEmpty()) { /* The asset exists, so this is a no-op. */ - WM_reportf(RPT_INFO, "%s: Will not overwrite existing asset %s", __func__, dst); + BKE_reportf(reports, RPT_INFO, "%s: Will not overwrite existing asset %s", __func__, dst); return true; } } @@ -157,86 +169,96 @@ bool copy_asset(const char *src, const char *dst, eUSDTexNameCollisionMode name_ pxr::ArResolvedPath src_path = ar.Resolve(src); if (src_path.IsEmpty()) { - WM_reportf(RPT_ERROR, "%s: Can't resolve path %s", __func__, src); + BKE_reportf(reports, RPT_ERROR, "%s: Can't resolve path %s", __func__, src); return false; } pxr::ArResolvedPath dst_path = ar.ResolveForNewAsset(dst); if (dst_path.IsEmpty()) { - WM_reportf(RPT_ERROR, "%s: Can't resolve path %s for writing", __func__, dst); + BKE_reportf(reports, RPT_ERROR, "%s: Can't resolve path %s for writing", __func__, dst); return false; } if (src_path == dst_path) { - WM_reportf(RPT_ERROR, - "%s: Can't copy %s. The source and destination paths are the same", - __func__, - src_path.GetPathString().c_str()); + BKE_reportf(reports, + RPT_ERROR, + "%s: Can't copy %s. The source and destination paths are the same", + __func__, + src_path.GetPathString().c_str()); return false; } std::string why_not; if (!ar.CanWriteAssetToPath(dst_path, &why_not)) { - WM_reportf(RPT_ERROR, - "%s: Can't write to asset %s: %s", - __func__, - dst_path.GetPathString().c_str(), - why_not.c_str()); + BKE_reportf(reports, + RPT_ERROR, + "%s: Can't write to asset %s: %s", + __func__, + dst_path.GetPathString().c_str(), + why_not.c_str()); return false; } std::shared_ptr src_asset = ar.OpenAsset(src_path); if (!src_asset) { - WM_reportf( - RPT_ERROR, "%s: Can't open source asset %s", __func__, src_path.GetPathString().c_str()); + BKE_reportf(reports, + RPT_ERROR, + "%s: Can't open source asset %s", + __func__, + src_path.GetPathString().c_str()); return false; } const size_t size = src_asset->GetSize(); if (size == 0) { - WM_reportf(RPT_WARNING, - "%s: Will not copy zero size source asset %s", - __func__, - src_path.GetPathString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Will not copy zero size source asset %s", + __func__, + src_path.GetPathString().c_str()); return false; } std::shared_ptr buf = src_asset->GetBuffer(); if (!buf) { - WM_reportf(RPT_ERROR, - "%s: Null buffer for source asset %s", - __func__, - src_path.GetPathString().c_str()); + BKE_reportf(reports, + RPT_ERROR, + "%s: Null buffer for source asset %s", + __func__, + src_path.GetPathString().c_str()); return false; } std::shared_ptr dst_asset = ar.OpenAssetForWrite( dst_path, pxr::ArResolver::WriteMode::Replace); if (!dst_asset) { - WM_reportf(RPT_ERROR, - "%s: Can't open destination asset %s for writing", - __func__, - src_path.GetPathString().c_str()); + BKE_reportf(reports, + RPT_ERROR, + "%s: Can't open destination asset %s for writing", + __func__, + src_path.GetPathString().c_str()); return false; } size_t bytes_written = dst_asset->Write(src_asset->GetBuffer().get(), src_asset->GetSize(), 0); if (bytes_written == 0) { - WM_reportf(RPT_ERROR, - "%s: Error writing to destination asset %s", - __func__, - dst_path.GetPathString().c_str()); + BKE_reportf(reports, + RPT_ERROR, + "%s: Error writing to destination asset %s", + __func__, + dst_path.GetPathString().c_str()); } if (!dst_asset->Close()) { - WM_reportf(RPT_ERROR, - "%s: Couldn't close destination asset %s", - __func__, - dst_path.GetPathString().c_str()); + BKE_reportf(reports, + RPT_ERROR, + "%s: Couldn't close destination asset %s", + __func__, + dst_path.GetPathString().c_str()); return false; } @@ -250,11 +272,15 @@ bool asset_exists(const char *path) std::string import_asset(const char *src, const char *import_dir, - eUSDTexNameCollisionMode name_collision_mode) + eUSDTexNameCollisionMode name_collision_mode, + ReportList *reports) { if (import_dir[0] == '\0') { - WM_reportf( - RPT_ERROR, "%s: Texture import directory path empty, couldn't import %s", __func__, src); + BKE_reportf(reports, + RPT_ERROR, + "%s: Texture import directory path empty, couldn't import %s", + __func__, + src); return src; } @@ -267,14 +293,15 @@ std::string import_asset(const char *src, basepath = BKE_main_blendfile_path_from_global(); if (!basepath || basepath[0] == '\0') { - WM_reportf(RPT_ERROR, - "%s: import directory is relative " - "but the blend file path is empty. " - "Please save the blend file before importing the USD " - "or provide an absolute import directory path. " - "Can't import %s", - __func__, - src); + BKE_reportf(reports, + RPT_ERROR, + "%s: import directory is relative " + "but the blend file path is empty. " + "Please save the blend file before importing the USD " + "or provide an absolute import directory path. " + "Can't import %s", + __func__, + src); return src; } BLI_path_abs(dest_dir_path, basepath); @@ -283,16 +310,19 @@ std::string import_asset(const char *src, BLI_path_normalize(dest_dir_path); if (!BLI_dir_create_recursive(dest_dir_path)) { - WM_reportf( - RPT_ERROR, "%s: Couldn't create texture import directory %s", __func__, dest_dir_path); + BKE_reportf(reports, + RPT_ERROR, + "%s: Couldn't create texture import directory %s", + __func__, + dest_dir_path); return src; } if (is_udim_path(src)) { - return copy_udim_asset_to_directory(src, dest_dir_path, name_collision_mode); + return copy_udim_asset_to_directory(src, dest_dir_path, name_collision_mode, reports); } - return copy_asset_to_directory(src, dest_dir_path, name_collision_mode); + return copy_asset_to_directory(src, dest_dir_path, name_collision_mode, reports); } bool is_udim_path(const std::string &path) diff --git a/source/blender/io/usd/intern/usd_asset_utils.h b/source/blender/io/usd/intern/usd_asset_utils.h index 2061342a398..b5c1b1d3733 100644 --- a/source/blender/io/usd/intern/usd_asset_utils.h +++ b/source/blender/io/usd/intern/usd_asset_utils.h @@ -17,9 +17,14 @@ namespace blender::io::usd { * \param src: source path of the asset to copy * \param dst: destination path of the copy * \param name_collision_mode: behavior when `dst` already exists + * \param reports: the storage for potential warning or error reports (generated using BKE_report + * API). * \return true if the copy succeeded, false otherwise */ -bool copy_asset(const char *src, const char *dst, eUSDTexNameCollisionMode name_collision_mode); +bool copy_asset(const char *src, + const char *dst, + eUSDTexNameCollisionMode name_collision_mode, + ReportList *reports); /** * Invoke the USD asset resolver to determine if the @@ -41,11 +46,14 @@ bool asset_exists(const char *path); * \param src: source path of the asset to import * \param import_dir: path to the destination directory * \param name_collision_mode: behavior when a file of the same name already exists + * \param reports: the storage for potential warning or error reports (generated using BKE_report + * API). * \return path to copied file or the original `src` path if there was an error */ std::string import_asset(const char *src, const char *import_dir, - eUSDTexNameCollisionMode name_collision_mode); + eUSDTexNameCollisionMode name_collision_mode, + ReportList *reports); /** * Check if the given path contains a UDIM token. diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc index fe81218f54d..353730a6095 100644 --- a/source/blender/io/usd/intern/usd_capi_export.cc +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -28,6 +28,7 @@ #include "BKE_blender_version.h" #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_report.h" #include "BKE_scene.h" #include "BLI_fileops.h" @@ -108,6 +109,8 @@ static bool prim_path_valid(const char *path) /** * Perform validation of export parameter settings. * \return true if the parameters are valid; returns false otherwise. + * + * \warning Do not call from worker thread, only from main thread (i.e. before starting the wmJob). */ static bool export_params_valid(const USDExportParams ¶ms) { @@ -183,33 +186,36 @@ static bool perform_usdz_conversion(const ExportJobData *data) if (BLI_exists(data->usdz_filepath)) { result = BLI_delete(data->usdz_filepath, false, false); if (result != 0) { - WM_reportf( - RPT_ERROR, "USD Export: Unable to delete existing usdz file %s", data->usdz_filepath); + BKE_reportf(data->params.worker_status->reports, + RPT_ERROR, + "USD Export: Unable to delete existing usdz file %s", + data->usdz_filepath); return false; } } result = BLI_path_move(usdz_temp_full_path, data->usdz_filepath); if (result != 0) { - WM_reportf(RPT_ERROR, - "USD Export: Couldn't move new usdz file from temporary location %s to %s", - usdz_temp_full_path, - data->usdz_filepath); + BKE_reportf(data->params.worker_status->reports, + RPT_ERROR, + "USD Export: Couldn't move new usdz file from temporary location %s to %s", + usdz_temp_full_path, + data->usdz_filepath); return false; } return true; } -static pxr::UsdStageRefPtr export_to_stage(const USDExportParams ¶ms, - Depsgraph *depsgraph, - const char *filepath, - wmJobWorkerStatus *worker_status) +pxr::UsdStageRefPtr export_to_stage(const USDExportParams ¶ms, + Depsgraph *depsgraph, + const char *filepath) { pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(filepath); if (!usd_stage) { return usd_stage; } + wmJobWorkerStatus *worker_status = params.worker_status; Scene *scene = DEG_get_input_scene(depsgraph); Main *bmain = DEG_get_bmain(depsgraph); @@ -273,7 +279,7 @@ static pxr::UsdStageRefPtr export_to_stage(const USDExportParams ¶ms, } } - call_export_hooks(usd_stage, depsgraph); + call_export_hooks(usd_stage, depsgraph, params.worker_status->reports); /* Finish up by going back to the keyframe that was current before we started. */ if (scene->r.cfra != orig_frame) { @@ -284,14 +290,6 @@ static pxr::UsdStageRefPtr export_to_stage(const USDExportParams ¶ms, return usd_stage; } -pxr::UsdStageRefPtr export_to_stage(const USDExportParams ¶ms, - Depsgraph *depsgraph, - const char *filepath) -{ - wmJobWorkerStatus worker_status = {}; - return export_to_stage(params, depsgraph, filepath, &worker_status); -} - static void export_startjob(void *customdata, wmJobWorkerStatus *worker_status) { ExportJobData *data = static_cast(customdata); @@ -315,16 +313,18 @@ static void export_startjob(void *customdata, wmJobWorkerStatus *worker_status) worker_status->progress = 0.0f; worker_status->do_update = true; + data->params.worker_status = worker_status; pxr::UsdStageRefPtr usd_stage = export_to_stage( - data->params, data->depsgraph, data->unarchived_filepath, worker_status); + data->params, data->depsgraph, data->unarchived_filepath); if (!usd_stage) { /* This happens when the USD JSON files cannot be found. When that happens, * the USD library doesn't know it has the functionality to write USDA and * USDC files, and creating a new UsdStage fails. */ - WM_reportf(RPT_ERROR, - "USD Export: unable to find suitable USD plugin to write %s", - data->unarchived_filepath); + BKE_reportf(worker_status->reports, + RPT_ERROR, + "USD Export: unable to find suitable USD plugin to write %s", + data->unarchived_filepath); return; } @@ -420,7 +420,8 @@ static void set_job_filepath(blender::io::usd::ExportJobData *job, const char *f bool USD_export(bContext *C, const char *filepath, const USDExportParams *params, - bool as_background_job) + bool as_background_job, + ReportList *reports) { if (!blender::io::usd::export_params_valid(*params)) { return false; @@ -469,6 +470,9 @@ bool USD_export(bContext *C, } else { wmJobWorkerStatus worker_status = {}; + /* Use the operator's reports in non-background case. */ + worker_status.reports = reports; + blender::io::usd::export_startjob(job, &worker_status); blender::io::usd::export_endjob(job); export_ok = job->export_ok; diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index b86a904b1f9..402c666c113 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -21,6 +21,7 @@ #include "BKE_main.h" #include "BKE_node.hh" #include "BKE_object.hh" +#include "BKE_report.h" #include "BKE_scene.h" #include "BKE_world.h" @@ -157,6 +158,8 @@ static void import_startjob(void *customdata, wmJobWorkerStatus *worker_status) data->archive = nullptr; data->start_time = timeit::Clock::now(); + data->params.worker_status = worker_status; + WM_set_locked_interface(data->wm, true); G.is_break = false; @@ -221,7 +224,10 @@ static void import_startjob(void *customdata, wmJobWorkerStatus *worker_status) pxr::UsdStage::OpenMasked(data->filepath, pop_mask); if (!stage) { - WM_reportf(RPT_ERROR, "USD Import: unable to open stage to read %s", data->filepath); + BKE_reportf(worker_status->reports, + RPT_ERROR, + "USD Import: unable to open stage to read %s", + data->filepath); data->import_ok = false; data->error_code = USD_ARCHIVE_FAIL; return; @@ -392,7 +398,9 @@ static void import_endjob(void *customdata) data->import_ok = !data->was_canceled; break; case USD_ARCHIVE_FAIL: - WM_report(RPT_ERROR, "Could not open USD archive for reading, see console for detail"); + BKE_report(data->params.worker_status->reports, + RPT_ERROR, + "Could not open USD archive for reading, see console for detail"); break; } @@ -417,7 +425,8 @@ using namespace blender::io::usd; bool USD_import(bContext *C, const char *filepath, const USDImportParams *params, - bool as_background_job) + bool as_background_job, + ReportList *reports) { /* Using new here since `MEM_*` functions do not call constructor to properly initialize data. */ ImportJobData *job = new ImportJobData(); @@ -460,6 +469,9 @@ bool USD_import(bContext *C, } else { wmJobWorkerStatus worker_status = {}; + /* Use the operator's reports in non-background case. */ + worker_status.reports = reports; + import_startjob(job, &worker_status); import_endjob(job); import_ok = job->import_ok; diff --git a/source/blender/io/usd/intern/usd_hook.cc b/source/blender/io/usd/intern/usd_hook.cc index a60ecf679ae..f075bdc7123 100644 --- a/source/blender/io/usd/intern/usd_hook.cc +++ b/source/blender/io/usd/intern/usd_hook.cc @@ -15,6 +15,8 @@ #include "BLI_listbase.h" +#include "BKE_report.h" + #include "RNA_access.hh" #include "RNA_prototypes.h" #include "RNA_types.hh" @@ -152,7 +154,7 @@ void register_export_hook_converters() } /* Retrieve and report the current Python error. */ -static void handle_python_error(USDHook *hook) +static void handle_python_error(USDHook *hook, ReportList *reports) { if (!PyErr_Occurred()) { return; @@ -160,9 +162,10 @@ static void handle_python_error(USDHook *hook) PyErr_Print(); - WM_reportf(RPT_ERROR, - "An exception occurred invoking USD hook '%s'. Please see the console for details", - hook->name); + BKE_reportf(reports, + RPT_ERROR, + "An exception occurred invoking USD hook '%s'. Please see the console for details", + hook->name); } /* Abstract base class to facilitate calling a function with a given @@ -207,10 +210,11 @@ class USDHookInvoker { call_hook(hook_obj); } catch (python::error_already_set const &) { - handle_python_error(hook); + handle_python_error(hook, reports_); } catch (...) { - WM_reportf(RPT_ERROR, "An exception occurred invoking USD hook '%s'", hook->name); + BKE_reportf( + reports_, RPT_ERROR, "An exception occurred invoking USD hook '%s'", hook->name); } } @@ -225,6 +229,9 @@ class USDHookInvoker { * * python::call_method(hook_obj, function_name(), arg1, arg2); */ virtual void call_hook(PyObject *hook_obj) const = 0; + + /* Reports list provided when constructing the subclass, used by #call() to store reports. */ + ReportList *reports_; }; class OnExportInvoker : public USDHookInvoker { @@ -232,9 +239,10 @@ class OnExportInvoker : public USDHookInvoker { USDSceneExportContext hook_context_; public: - OnExportInvoker(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph) + OnExportInvoker(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph, ReportList *reports) : hook_context_(stage, depsgraph) { + reports_ = reports; } protected: @@ -258,10 +266,12 @@ class OnMaterialExportInvoker : public USDHookInvoker { public: OnMaterialExportInvoker(pxr::UsdStageRefPtr stage, Material *material, - pxr::UsdShadeMaterial &usd_material) + pxr::UsdShadeMaterial &usd_material, + ReportList *reports) : hook_context_(stage), usd_material_(usd_material) { material_ptr_ = RNA_pointer_create(nullptr, &RNA_Material, material); + reports_ = reports; } protected: @@ -277,25 +287,26 @@ class OnMaterialExportInvoker : public USDHookInvoker { } }; -void call_export_hooks(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph) +void call_export_hooks(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph, ReportList *reports) { if (g_usd_hooks.empty()) { return; } - OnExportInvoker on_export(stage, depsgraph); + OnExportInvoker on_export(stage, depsgraph, reports); on_export.call(); } void call_material_export_hooks(pxr::UsdStageRefPtr stage, Material *material, - pxr::UsdShadeMaterial &usd_material) + pxr::UsdShadeMaterial &usd_material, + ReportList *reports) { if (g_usd_hooks.empty()) { return; } - OnMaterialExportInvoker on_material_export(stage, material, usd_material); + OnMaterialExportInvoker on_material_export(stage, material, usd_material, reports); on_material_export.call(); } diff --git a/source/blender/io/usd/intern/usd_hook.h b/source/blender/io/usd/intern/usd_hook.h index 8f53fd2c399..e8c3c3a5b32 100644 --- a/source/blender/io/usd/intern/usd_hook.h +++ b/source/blender/io/usd/intern/usd_hook.h @@ -11,6 +11,7 @@ struct Depsgraph; struct ExportJobData; struct Material; +struct ReportList; struct USDExportParams; namespace blender::io::usd { @@ -19,11 +20,12 @@ namespace blender::io::usd { void register_export_hook_converters(); /** Call the 'on_export' chaser function defined in the registered USDHook classes. */ -void call_export_hooks(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph); +void call_export_hooks(pxr::UsdStageRefPtr stage, Depsgraph *depsgraph, ReportList *reports); /** Call the 'on_material_export' hook functions defined in the registered #USDHook classes. */ void call_material_export_hooks(pxr::UsdStageRefPtr stage, Material *material, - pxr::UsdShadeMaterial &usd_material); + pxr::UsdShadeMaterial &usd_material, + ReportList *reports); } // 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 25b804af9e0..c9534114a74 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -760,7 +760,7 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, USD_TEX_NAME_COLLISION_OVERWRITE : params_.tex_name_collision_mode; - file_path = import_asset(file_path.c_str(), textures_dir, name_collision_mode); + file_path = import_asset(file_path.c_str(), textures_dir, name_collision_mode, reports()); } /* If this is a UDIM texture, this will store the diff --git a/source/blender/io/usd/intern/usd_reader_material.h b/source/blender/io/usd/intern/usd_reader_material.h index 0b570bfc735..53fcc4b01d1 100644 --- a/source/blender/io/usd/intern/usd_reader_material.h +++ b/source/blender/io/usd/intern/usd_reader_material.h @@ -5,6 +5,8 @@ #include "usd.h" +#include "WM_types.hh" + #include "BLI_map.hh" #include @@ -86,6 +88,12 @@ class USDMaterialReader { Material *add_material(const pxr::UsdShadeMaterial &usd_material) const; + /** Get the wmJobWorkerStatus-provided `reports` list pointer, to use with the BKE_report API. */ + ReportList *reports() const + { + return params_.worker_status->reports; + } + protected: /** Create the Principled BSDF shader node network. */ void import_usd_preview(Material *mtl, const pxr::UsdShadeShader &usd_shader) const; diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 56fea8a7fea..3e91c36235a 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -15,6 +15,7 @@ #include "BKE_material.h" #include "BKE_mesh.hh" #include "BKE_object.hh" +#include "BKE_report.h" #include "BLI_math_color.hh" #include "BLI_math_geom.h" @@ -165,7 +166,7 @@ USDMeshReader::USDMeshReader(const pxr::UsdPrim &prim, } static std::optional convert_usd_type_to_blender( - const pxr::SdfValueTypeName usd_type) + const pxr::SdfValueTypeName usd_type, ReportList *reports) { static const blender::Map type_map = []() { blender::Map map; @@ -194,7 +195,10 @@ static std::optional convert_usd_type_to_blender( const eCustomDataType *value = type_map.lookup_ptr(usd_type); if (value == nullptr) { - WM_reportf(RPT_WARNING, "Unsupported type %s for mesh data", usd_type.GetAsToken().GetText()); + BKE_reportf(reports, + RPT_WARNING, + "Unsupported type %s for mesh data", + usd_type.GetAsToken().GetText()); return std::nullopt; } @@ -202,7 +206,7 @@ static std::optional convert_usd_type_to_blender( } static const std::optional convert_usd_varying_to_blender( - const pxr::TfToken usd_domain) + const pxr::TfToken usd_domain, ReportList *reports) { static const blender::Map domain_map = []() { blender::Map map; @@ -221,7 +225,8 @@ static const std::optional convert_usd_varying_to_blender( const eAttrDomain *value = domain_map.lookup_ptr(usd_domain); if (value == nullptr) { - WM_reportf(RPT_WARNING, "Unsupported domain for mesh data type %s", usd_domain.GetText()); + BKE_reportf( + reports, RPT_WARNING, "Unsupported domain for mesh data type %s", usd_domain.GetText()); return std::nullopt; } @@ -271,11 +276,11 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime) } if (import_params_.import_blendshapes) { - import_blendshapes(bmain, object_, prim_); + import_blendshapes(bmain, object_, prim_, reports()); } if (import_params_.import_skeletons) { - import_mesh_skel_bindings(bmain, object_, prim_); + import_mesh_skel_bindings(bmain, object_, prim_, reports()); } USDXformReader::read_object_data(bmain, motionSampleTime); @@ -349,22 +354,26 @@ void USDMeshReader::read_mpolys(Mesh *mesh) template pxr::VtArray get_prim_attribute_array(const pxr::UsdGeomPrimvar &primvar, - const double motionSampleTime) + const double motionSampleTime, + ReportList *reports) { pxr::VtArray array; pxr::VtValue primvar_val; if (!primvar.ComputeFlattened(&primvar_val, motionSampleTime)) { - WM_reportf( - RPT_WARNING, "Unable to get array values for primvar %s", primvar.GetName().GetText()); + BKE_reportf(reports, + RPT_WARNING, + "Unable to get array values for primvar %s", + primvar.GetName().GetText()); return array; } if (!primvar_val.CanCast>()) { - WM_reportf(RPT_WARNING, - "USD Import: can't cast attribute '%s' to array", - primvar.GetName().GetText()); + BKE_reportf(reports, + RPT_WARNING, + "USD Import: can't cast attribute '%s' to array", + primvar.GetName().GetText()); return array; } @@ -380,8 +389,8 @@ void USDMeshReader::read_color_data_primvar(Mesh *mesh, return; } - pxr::VtArray usd_colors = get_prim_attribute_array(primvar, - motionSampleTime); + pxr::VtArray usd_colors = get_prim_attribute_array( + primvar, motionSampleTime, reports()); if (usd_colors.empty()) { return; @@ -395,9 +404,11 @@ void USDMeshReader::read_color_data_primvar(Mesh *mesh, (interp == pxr::UsdGeomTokens->constant && usd_colors.size() != 1) || (interp == pxr::UsdGeomTokens->uniform && usd_colors.size() != mesh->faces_num)) { - WM_reportf(RPT_WARNING, - "USD Import: color attribute value '%s' count inconsistent with interpolation type", - primvar.GetName().GetText()); + BKE_reportf( + reports(), + RPT_WARNING, + "USD Import: color attribute value '%s' count inconsistent with interpolation type", + primvar.GetName().GetText()); return; } @@ -418,9 +429,10 @@ void USDMeshReader::read_color_data_primvar(Mesh *mesh, color_data = attributes.lookup_or_add_for_write_only_span(primvar_name, color_domain); if (!color_data) { - WM_reportf(RPT_WARNING, - "USD Import: couldn't add color attribute '%s'", - primvar.GetBaseName().GetText()); + BKE_reportf(reports(), + RPT_WARNING, + "USD Import: couldn't add color attribute '%s'", + primvar.GetBaseName().GetText()); return; } @@ -494,8 +506,8 @@ void USDMeshReader::read_uv_data_primvar(Mesh *mesh, { const StringRef primvar_name(primvar.StripPrimvarsName(primvar.GetName()).GetString()); - pxr::VtArray usd_uvs = get_prim_attribute_array(primvar, - motionSampleTime); + pxr::VtArray usd_uvs = get_prim_attribute_array( + primvar, motionSampleTime, reports()); if (usd_uvs.empty()) { return; @@ -511,9 +523,10 @@ void USDMeshReader::read_uv_data_primvar(Mesh *mesh, (varying_type == pxr::UsdGeomTokens->vertex && usd_uvs.size() != mesh->totvert) || (varying_type == pxr::UsdGeomTokens->varying && usd_uvs.size() != mesh->totloop)) { - WM_reportf(RPT_WARNING, - "USD Import: UV attribute value '%s' count inconsistent with interpolation type", - primvar.GetName().GetText()); + BKE_reportf(reports(), + RPT_WARNING, + "USD Import: UV attribute value '%s' count inconsistent with interpolation type", + primvar.GetName().GetText()); return; } @@ -522,9 +535,10 @@ void USDMeshReader::read_uv_data_primvar(Mesh *mesh, primvar_name, ATTR_DOMAIN_CORNER); if (!uv_data) { - WM_reportf(RPT_WARNING, - "USD Import: couldn't add UV attribute '%s'", - primvar.GetBaseName().GetText()); + BKE_reportf(reports(), + RPT_WARNING, + "USD Import: couldn't add UV attribute '%s'", + primvar.GetBaseName().GetText()); return; } @@ -588,10 +602,13 @@ void USDMeshReader::copy_prim_array_to_blender_attribute(const Mesh *mesh, MutableSpan attribute) { const pxr::TfToken interp = primvar.GetInterpolation(); - pxr::VtArray primvar_array = get_prim_attribute_array(primvar, motionSampleTime); + pxr::VtArray primvar_array = get_prim_attribute_array( + primvar, motionSampleTime, reports()); if (primvar_array.empty()) { - WM_reportf( - RPT_WARNING, "Unable to get array values for primvar %s", primvar.GetName().GetText()); + BKE_reportf(reports(), + RPT_WARNING, + "Unable to get array values for primvar %s", + primvar.GetName().GetText()); return; } @@ -642,8 +659,9 @@ void USDMeshReader::read_generic_data_primvar(Mesh *mesh, const pxr::TfToken varying_type = primvar.GetInterpolation(); const pxr::TfToken name = pxr::UsdGeomPrimvar::StripPrimvarsName(primvar.GetPrimvarName()); - const std::optional domain = convert_usd_varying_to_blender(varying_type); - const std::optional type = convert_usd_type_to_blender(sdf_type); + const std::optional domain = convert_usd_varying_to_blender(varying_type, + reports()); + const std::optional type = convert_usd_type_to_blender(sdf_type, reports()); if (!domain.has_value() || !type.has_value()) { return; @@ -678,10 +696,11 @@ void USDMeshReader::read_generic_data_primvar(Mesh *mesh, mesh, primvar, motionSampleTime, attribute.span.typed()); break; default: - WM_reportf(RPT_ERROR, - "Generic primvar %s: invalid type %s", - primvar.GetName().GetText(), - sdf_type.GetAsToken().GetText()); + BKE_reportf(reports(), + RPT_ERROR, + "Generic primvar %s: invalid type %s", + primvar.GetName().GetText(), + sdf_type.GetAsToken().GetText()); break; } attribute.finish(); @@ -878,10 +897,11 @@ void USDMeshReader::read_custom_data(const ImportSettings *settings, /* Convert primvars to custom layer data. */ for (pxr::UsdGeomPrimvar &pv : primvars) { if (!pv.HasValue()) { - WM_reportf(RPT_WARNING, - "Skipping primvar %s, mesh %s -- no value", - pv.GetName().GetText(), - &mesh->id.name[2]); + BKE_reportf(reports(), + RPT_WARNING, + "Skipping primvar %s, mesh %s -- no value", + pv.GetName().GetText(), + &mesh->id.name[2]); continue; } @@ -898,7 +918,7 @@ void USDMeshReader::read_custom_data(const ImportSettings *settings, } /* Read Color primvars. */ - if (convert_usd_type_to_blender(type) == CD_PROP_COLOR) { + if (convert_usd_type_to_blender(type, reports()) == CD_PROP_COLOR) { if ((settings->read_flag & MOD_MESHSEQ_READ_COLOR) != 0) { /* Set the active color name to 'displayColor', if a color primvar * with this name exists. Otherwise, use the name of the first @@ -916,7 +936,7 @@ void USDMeshReader::read_custom_data(const ImportSettings *settings, pxr::UsdGeomTokens->vertex, pxr::UsdGeomTokens->faceVarying, pxr::UsdGeomTokens->varying) && - convert_usd_type_to_blender(type) == CD_PROP_FLOAT2) + convert_usd_type_to_blender(type, reports()) == CD_PROP_FLOAT2) { if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { /* Set the active uv set name to 'st', if a uv set primvar @@ -1139,10 +1159,11 @@ std::optional USDMeshReader::get_local_usd_xform(const float time) return XformResult(pxr::GfMatrix4f(bind_xf), true); } else { - WM_reportf(RPT_WARNING, - "%s: Couldn't compute geom bind transform for %s", - __func__, - prim_.GetPath().GetAsString().c_str()); + BKE_reportf(reports(), + RPT_WARNING, + "%s: Couldn't compute geom bind transform for %s", + __func__, + prim_.GetPath().GetAsString().c_str()); } } } diff --git a/source/blender/io/usd/intern/usd_reader_prim.h b/source/blender/io/usd/intern/usd_reader_prim.h index df896f09dec..62b5add840a 100644 --- a/source/blender/io/usd/intern/usd_reader_prim.h +++ b/source/blender/io/usd/intern/usd_reader_prim.h @@ -9,6 +9,8 @@ #include "usd.h" +#include "WM_types.hh" + #include #include @@ -113,6 +115,12 @@ class USDPrimReader { parent_reader_ = parent; } + /** Get the wmJobWorkerStatus-provided `reports` list pointer, to use with the BKE_report API. */ + ReportList *reports() const + { + return import_params_.worker_status->reports; + } + /* Since readers might be referenced through handles * maintained by modifiers and constraints, we provide * a reference count to facilitate managing the object diff --git a/source/blender/io/usd/intern/usd_reader_shape.cc b/source/blender/io/usd/intern/usd_reader_shape.cc index 50d1a0b32e0..e09b47ea9fa 100644 --- a/source/blender/io/usd/intern/usd_reader_shape.cc +++ b/source/blender/io/usd/intern/usd_reader_shape.cc @@ -6,6 +6,7 @@ #include "BKE_mesh.hh" #include "BKE_modifier.h" #include "BKE_object.hh" +#include "BKE_report.h" #include "DNA_cachefile_types.h" #include "DNA_mesh_types.h" @@ -118,10 +119,11 @@ bool USDShapeReader::read_mesh_values(double motionSampleTime, return true; } - WM_reportf(RPT_ERROR, - "Unhandled Gprim type: %s (%s)", - prim_.GetTypeName().GetText(), - prim_.GetPath().GetText()); + BKE_reportf(reports(), + RPT_ERROR, + "Unhandled Gprim type: %s (%s)", + prim_.GetTypeName().GetText(), + prim_.GetPath().GetText()); return false; } @@ -230,10 +232,11 @@ bool USDShapeReader::is_time_varying() return geom.GetRadiusAttr().ValueMightBeTimeVarying(); } - WM_reportf(RPT_ERROR, - "Unhandled Gprim type: %s (%s)", - prim_.GetTypeName().GetText(), - prim_.GetPath().GetText()); + BKE_reportf(reports(), + RPT_ERROR, + "Unhandled Gprim type: %s (%s)", + prim_.GetTypeName().GetText(), + prim_.GetPath().GetText()); return false; } diff --git a/source/blender/io/usd/intern/usd_reader_skeleton.cc b/source/blender/io/usd/intern/usd_reader_skeleton.cc index 28f55bc11fa..797330a90f5 100644 --- a/source/blender/io/usd/intern/usd_reader_skeleton.cc +++ b/source/blender/io/usd/intern/usd_reader_skeleton.cc @@ -39,7 +39,7 @@ void USDSkeletonReader::read_object_data(Main *bmain, const double motionSampleT return; } - import_skeleton(bmain, object_, skel_); + import_skeleton(bmain, object_, skel_, reports()); USDXformReader::read_object_data(bmain, motionSampleTime); } diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index a908e5b11d6..cb6e6735412 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -44,6 +44,7 @@ #include "BKE_lib_id.h" #include "BKE_modifier.h" +#include "BKE_report.h" #include "DNA_material_types.h" @@ -366,10 +367,11 @@ void USDStageReader::process_armature_modifiers() const std::string skel_path = mesh_reader->get_skeleton_path(); std::map::const_iterator it = usd_path_to_armature.find(skel_path); if (it == usd_path_to_armature.end()) { - WM_reportf(RPT_WARNING, - "%s: Couldn't find armature object corresponding to USD skeleton %s", - __func__, - skel_path.c_str()); + BKE_reportf(reports(), + RPT_WARNING, + "%s: Couldn't find armature object corresponding to USD skeleton %s", + __func__, + skel_path.c_str()); } amd->object = it->second; } diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h index b0d50f0902a..cc409271ff9 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.h +++ b/source/blender/io/usd/intern/usd_reader_stage.h @@ -5,6 +5,8 @@ struct Main; +#include "WM_types.hh" + #include "usd.h" #include "usd_reader_prim.h" @@ -79,6 +81,12 @@ class USDStageReader { return settings_; } + /** Get the wmJobWorkerStatus-provided `reports` list pointer, to use with the BKE_report API. */ + ReportList *reports() const + { + return params_.worker_status->reports; + } + void clear_readers(); const std::vector &readers() const diff --git a/source/blender/io/usd/intern/usd_skel_convert.cc b/source/blender/io/usd/intern/usd_skel_convert.cc index d0da2a68606..de60076a648 100644 --- a/source/blender/io/usd/intern/usd_skel_convert.cc +++ b/source/blender/io/usd/intern/usd_skel_convert.cc @@ -32,6 +32,7 @@ #include "BKE_modifier.h" #include "BKE_object.hh" #include "BKE_object_deform.h" +#include "BKE_report.h" #include "BLI_math_vector.h" #include "BLI_string.h" @@ -103,11 +104,14 @@ void add_bezt(FCurve *fcu, * \param arm_obj: Armature object to which the action will be added * \param skel_query: The USD skeleton query for reading the animation * \param joint_to_bone_map: Map a USD skeleton joint name to a bone name + * \param reports: the storage for potential warning or error reports (generated using BKE_report + * API). */ void import_skeleton_curves(Main *bmain, Object *arm_obj, const pxr::UsdSkelSkeletonQuery &skel_query, - const std::map &joint_to_bone_map) + const std::map &joint_to_bone_map, + ReportList *reports) { if (!(bmain && arm_obj && skel_query)) { @@ -218,18 +222,20 @@ void import_skeleton_curves(Main *bmain, /* Get the world space joint transforms at bind time. */ pxr::VtMatrix4dArray bind_xforms; if (!skel_query.GetJointWorldBindTransforms(&bind_xforms)) { - WM_reportf(RPT_WARNING, - "%s: Couldn't get world bind transforms for skeleton %s", - __func__, - skel_query.GetSkeleton().GetPrim().GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Couldn't get world bind transforms for skeleton %s", + __func__, + skel_query.GetSkeleton().GetPrim().GetPath().GetAsString().c_str()); return; } if (bind_xforms.size() != joint_order.size()) { - WM_reportf(RPT_WARNING, - "%s: Number of bind transforms doesn't match the number of joints for skeleton %s", - __func__, - skel_query.GetSkeleton().GetPrim().GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Number of bind transforms doesn't match the number of joints for skeleton %s", + __func__, + skel_query.GetSkeleton().GetPrim().GetPath().GetAsString().c_str()); return; } @@ -334,6 +340,7 @@ namespace blender::io::usd { void import_blendshapes(Main *bmain, Object *mesh_obj, const pxr::UsdPrim &prim, + ReportList *reports, const bool import_anim) { if (!(mesh_obj && mesh_obj->data && mesh_obj->type == OB_MESH && prim)) { @@ -363,10 +370,11 @@ void import_blendshapes(Main *bmain, pxr::SdfPathVector targets; if (!skel_api.GetBlendShapeTargetsRel().GetTargets(&targets)) { - WM_reportf(RPT_WARNING, - "%s: Couldn't get blendshape targets for prim %s", - __func__, - prim.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Couldn't get blendshape targets for prim %s", + __func__, + prim.GetPath().GetAsString().c_str()); return; } @@ -390,20 +398,22 @@ void import_blendshapes(Main *bmain, /* Sanity check. */ if (targets.size() != blendshapes.size()) { - WM_reportf(RPT_WARNING, - "%s: Number of blendshapes doesn't match number of blendshape targets for prim %s", - __func__, - prim.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Number of blendshapes doesn't match number of blendshape targets for prim %s", + __func__, + prim.GetPath().GetAsString().c_str()); return; } pxr::UsdStageRefPtr stage = prim.GetStage(); if (!stage) { - WM_reportf(RPT_WARNING, - "%s: Couldn't get stage for prim %s", - __func__, - prim.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Couldn't get stage for prim %s", + __func__, + prim.GetPath().GetAsString().c_str()); return; } @@ -440,16 +450,20 @@ void import_blendshapes(Main *bmain, pxr::VtVec3fArray offsets; if (!blendshape.GetOffsetsAttr().Get(&offsets)) { - WM_reportf(RPT_WARNING, - "%s: Couldn't get offsets for blend shape %s", - __func__, - path.GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Couldn't get offsets for blend shape %s", + __func__, + path.GetAsString().c_str()); continue; } if (offsets.empty()) { - WM_reportf( - RPT_WARNING, "%s: No offsets for blend shape %s", __func__, path.GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: No offsets for blend shape %s", + __func__, + path.GetAsString().c_str()); continue; } @@ -473,7 +487,8 @@ void import_blendshapes(Main *bmain, * offset to the key block point. */ for (int a = 0; a < kb->totelem; ++a, fp += 3) { if (a >= offsets.size()) { - WM_reportf( + BKE_reportf( + reports, RPT_WARNING, "%s: Number of offsets greater than number of mesh vertices for blend shape %s", __func__, @@ -495,7 +510,8 @@ void import_blendshapes(Main *bmain, continue; } if (a >= offsets.size()) { - WM_reportf( + BKE_reportf( + reports, RPT_WARNING, "%s: Number of offsets greater than number of mesh vertices for blend shape %s", __func__, @@ -621,6 +637,7 @@ void import_blendshapes(Main *bmain, void import_skeleton(Main *bmain, Object *arm_obj, const pxr::UsdSkelSkeleton &skel, + ReportList *reports, const bool import_anim) { if (!(arm_obj && arm_obj->data && arm_obj->type == OB_ARMATURE)) { @@ -631,10 +648,11 @@ void import_skeleton(Main *bmain, pxr::UsdSkelSkeletonQuery skel_query = skel_cache.GetSkelQuery(skel); if (!skel_query.IsValid()) { - WM_reportf(RPT_WARNING, - "%s: Couldn't query skeleton %s", - __func__, - skel.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Couldn't query skeleton %s", + __func__, + skel.GetPath().GetAsString().c_str()); return; } @@ -643,10 +661,11 @@ void import_skeleton(Main *bmain, pxr::VtTokenArray joint_order = skel_query.GetJointOrder(); if (joint_order.size() != skel_topology.size()) { - WM_reportf(RPT_WARNING, - "%s: Topology and joint order size mismatch for skeleton %s", - __func__, - skel.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Topology and joint order size mismatch for skeleton %s", + __func__, + skel.GetPath().GetAsString().c_str()); return; } @@ -668,8 +687,11 @@ void import_skeleton(Main *bmain, std::string name = pxr::SdfPath(joint).GetName(); EditBone *bone = ED_armature_ebone_add(arm, name.c_str()); if (!bone) { - WM_reportf( - RPT_WARNING, "%s: Couldn't add bone for joint %s", __func__, joint.GetString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Couldn't add bone for joint %s", + __func__, + joint.GetString().c_str()); edit_bones.push_back(nullptr); continue; } @@ -680,28 +702,31 @@ void import_skeleton(Main *bmain, /* Sanity check: we should have created a bone for each joint. */ const size_t num_joints = skel_topology.GetNumJoints(); if (edit_bones.size() != num_joints) { - WM_reportf(RPT_WARNING, - "%s: Mismatch in bone and joint counts for skeleton %s", - __func__, - skel.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Mismatch in bone and joint counts for skeleton %s", + __func__, + skel.GetPath().GetAsString().c_str()); return; } /* Get the world space joint transforms at bind time. */ pxr::VtMatrix4dArray bind_xforms; if (!skel_query.GetJointWorldBindTransforms(&bind_xforms)) { - WM_reportf(RPT_WARNING, - "%s: Couldn't get world bind transforms for skeleton %s", - __func__, - skel.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Couldn't get world bind transforms for skeleton %s", + __func__, + skel.GetPath().GetAsString().c_str()); return; } if (bind_xforms.size() != num_joints) { - WM_reportf(RPT_WARNING, - "%s: Mismatch in bind xforms and joint counts for skeleton %s", - __func__, - skel.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Mismatch in bind xforms and joint counts for skeleton %s", + __func__, + skel.GetPath().GetAsString().c_str()); return; } @@ -746,7 +771,8 @@ void import_skeleton(Main *bmain, bool valid_skeleton = true; if (negative_determinant) { valid_skeleton = false; - WM_reportf( + BKE_reportf( + reports, RPT_WARNING, "USD Skeleton Import: bone matrices with negative determinants detected in prim %s. " "Such matrices may indicate negative scales, possibly due to mirroring operations, " @@ -850,11 +876,14 @@ void import_skeleton(Main *bmain, ED_armature_edit_free(arm); if (import_anim && valid_skeleton) { - import_skeleton_curves(bmain, arm_obj, skel_query, joint_to_bone_map); + import_skeleton_curves(bmain, arm_obj, skel_query, joint_to_bone_map, reports); } } -void import_mesh_skel_bindings(Main *bmain, Object *mesh_obj, const pxr::UsdPrim &prim) +void import_mesh_skel_bindings(Main *bmain, + Object *mesh_obj, + const pxr::UsdPrim &prim, + ReportList *reports) { if (!(bmain && mesh_obj && mesh_obj->type == OB_MESH && prim)) { return; @@ -912,10 +941,11 @@ void import_mesh_skel_bindings(Main *bmain, Object *mesh_obj, const pxr::UsdPrim /* We expect the element counts to match. */ if (joint_indices_elem_size != joint_weights_elem_size) { - WM_reportf(RPT_WARNING, - "%s: Joint weights and joint indices element size mismatch for prim %s", - __func__, - prim.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Joint weights and joint indices element size mismatch for prim %s", + __func__, + prim.GetPath().GetAsString().c_str()); return; } @@ -931,10 +961,11 @@ void import_mesh_skel_bindings(Main *bmain, Object *mesh_obj, const pxr::UsdPrim } if (joint_indices.size() != joint_weights.size()) { - WM_reportf(RPT_WARNING, - "%s: Joint weights and joint indices size mismatch for prim %s", - __func__, - prim.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Joint weights and joint indices size mismatch for prim %s", + __func__, + prim.GetPath().GetAsString().c_str()); return; } @@ -944,11 +975,12 @@ void import_mesh_skel_bindings(Main *bmain, Object *mesh_obj, const pxr::UsdPrim /* Sanity check: we expect only vertex or constant interpolation. */ if (!ELEM(interp, pxr::UsdGeomTokens->vertex, pxr::UsdGeomTokens->constant)) { - WM_reportf(RPT_WARNING, - "%s: Unexpected joint weights interpolation type %s for prim %s", - __func__, - interp.GetString().c_str(), - prim.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Unexpected joint weights interpolation type %s for prim %s", + __func__, + interp.GetString().c_str(), + prim.GetPath().GetAsString().c_str()); return; } @@ -956,18 +988,20 @@ void import_mesh_skel_bindings(Main *bmain, Object *mesh_obj, const pxr::UsdPrim if (interp == pxr::UsdGeomTokens->vertex && joint_weights.size() != mesh->totvert * joint_weights_elem_size) { - WM_reportf(RPT_WARNING, - "%s: Joint weights of unexpected size for vertex interpolation for prim %s", - __func__, - prim.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Joint weights of unexpected size for vertex interpolation for prim %s", + __func__, + prim.GetPath().GetAsString().c_str()); return; } if (interp == pxr::UsdGeomTokens->constant && joint_weights.size() != joint_weights_elem_size) { - WM_reportf(RPT_WARNING, - "%s: Joint weights of unexpected size for constant interpolation for prim %s", - __func__, - prim.GetPath().GetAsString().c_str()); + BKE_reportf(reports, + RPT_WARNING, + "%s: Joint weights of unexpected size for constant interpolation for prim %s", + __func__, + prim.GetPath().GetAsString().c_str()); return; } @@ -989,10 +1023,11 @@ void import_mesh_skel_bindings(Main *bmain, Object *mesh_obj, const pxr::UsdPrim } if (BKE_object_defgroup_data_create(static_cast(mesh_obj->data)) == nullptr) { - WM_reportf(RPT_WARNING, - "%s: Error creating deform group data for mesh %s", - __func__, - mesh_obj->id.name + 2); + BKE_reportf(reports, + RPT_WARNING, + "%s: Error creating deform group data for mesh %s", + __func__, + mesh_obj->id.name + 2); return; } diff --git a/source/blender/io/usd/intern/usd_skel_convert.h b/source/blender/io/usd/intern/usd_skel_convert.h index aa130980b15..a2fd237a877 100644 --- a/source/blender/io/usd/intern/usd_skel_convert.h +++ b/source/blender/io/usd/intern/usd_skel_convert.h @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once +#include "DNA_windowmanager_types.h" #include #include #include @@ -35,12 +36,15 @@ struct ImportSettings; * \param bmain: Main pointer * \param mesh_obj: Mesh object to which imported shape keys will be added * \param prim: The USD primitive from which blend-shapes will be imported + * \param reports: the storage for potential warning or error reports (generated using BKE_report + * API). * \param import_anim: Whether to import time-sampled weights as shape key * animation curves */ void import_blendshapes(Main *bmain, Object *mesh_obj, const pxr::UsdPrim &prim, + ReportList *reports, bool import_anim = true); /** @@ -51,12 +55,15 @@ void import_blendshapes(Main *bmain, * \param bmain: Main pointer * \param arm_obj: Armature object to which the bone hierarchy will be added * \param skel: The USD skeleton from which bones and animation will be imported + * \param reports: the storage for potential warning or error reports (generated using BKE_report + * API). * \param import_anim: Whether to import time-sampled joint transforms as bone * animation curves */ void import_skeleton(Main *bmain, Object *arm_obj, const pxr::UsdSkelSkeleton &skel, + ReportList *reports, bool import_anim = true); /** * Import skinning data from a source USD prim as deform groups and an armature @@ -66,7 +73,12 @@ void import_skeleton(Main *bmain, * \param bmain: Main pointer * \param obj: Mesh object to which an armature modifier will be added * \param prim: The USD primitive from which skinning data will be imported + * \param reports: the storage for potential warning or error reports (generated using BKE_report + * API). */ -void import_mesh_skel_bindings(Main *bmain, Object *mesh_obj, const pxr::UsdPrim &prim); +void import_mesh_skel_bindings(Main *bmain, + Object *mesh_obj, + const pxr::UsdPrim &prim, + ReportList *reports); } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc index 4c41f8f67a0..7140f51ef3b 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.cc +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -9,6 +9,8 @@ #include #include "BKE_customdata.h" +#include "BKE_report.h" + #include "BLI_assert.h" #include "DNA_mesh_types.h" @@ -167,9 +169,10 @@ void USDAbstractWriter::author_extent(const pxr::UsdTimeCode timecode, pxr::UsdG pxr::GfBBox3d bounds = bboxCache.ComputeLocalBound(prim.GetPrim()); if (pxr::GfBBox3d() == bounds) { /* This will occur, for example, if a mesh does not have any vertices. */ - WM_reportf(RPT_WARNING, - "USD Export: no bounds could be computed for %s", - prim.GetPrim().GetName().GetText()); + BKE_reportf(reports(), + RPT_WARNING, + "USD Export: no bounds could be computed for %s", + prim.GetPrim().GetName().GetText()); return; } diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index b1f8a0e2fb4..94f5c2224c7 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -16,7 +16,10 @@ #include "DEG_depsgraph_query.hh" +#include "WM_types.hh" + #include "DNA_material_types.h" +#include "DNA_windowmanager_types.h" struct Material; @@ -51,6 +54,12 @@ class USDAbstractWriter : public AbstractHierarchyWriter { const pxr::SdfPath &usd_path() const; + /** Get the wmJobWorkerStatus-provided `reports` list pointer, to use with the BKE_report API. */ + ReportList *reports() const + { + return usd_export_context_.export_params.worker_status->reports; + } + protected: virtual void do_write(HierarchyContext &context) = 0; std::string get_export_file_path() const; diff --git a/source/blender/io/usd/intern/usd_writer_curves.cc b/source/blender/io/usd/intern/usd_writer_curves.cc index 77f5e07ba08..1c2d0119b11 100644 --- a/source/blender/io/usd/intern/usd_writer_curves.cc +++ b/source/blender/io/usd/intern/usd_writer_curves.cc @@ -18,6 +18,7 @@ #include "BKE_curves.hh" #include "BKE_lib_id.h" #include "BKE_material.h" +#include "BKE_report.h" #include "BLI_math_geom.h" #include "BLT_translation.h" @@ -26,7 +27,6 @@ #include "RNA_enum_types.hh" #include "WM_api.hh" -#include "WM_types.hh" namespace blender::io::usd { @@ -85,7 +85,8 @@ static void populate_curve_widths(const bke::CurvesGeometry &geometry, pxr::VtAr static pxr::TfToken get_curve_width_interpolation(const pxr::VtArray &widths, const pxr::VtArray &segments, const pxr::VtIntArray &control_point_counts, - const bool is_cyclic) + const bool is_cyclic, + ReportList *reports) { if (widths.empty()) { return pxr::TfToken(); @@ -110,7 +111,7 @@ static pxr::TfToken get_curve_width_interpolation(const pxr::VtArray &wid return pxr::UsdGeomTokens->varying; } - WM_report(RPT_WARNING, "Curve width size not supported for USD interpolation"); + BKE_report(reports, RPT_WARNING, "Curve width size not supported for USD interpolation"); return pxr::TfToken(); } @@ -158,7 +159,8 @@ static void populate_curve_props(const bke::CurvesGeometry &geometry, pxr::VtArray &widths, pxr::TfToken &interpolation, const bool is_cyclic, - const bool is_cubic) + const bool is_cubic, + ReportList *reports) { const int num_curves = geometry.curve_num; const Span positions = geometry.positions(); @@ -169,7 +171,8 @@ static void populate_curve_props(const bke::CurvesGeometry &geometry, geometry, positions, verts, control_point_counts, segments, is_cyclic, is_cubic); populate_curve_widths(geometry, widths); - interpolation = get_curve_width_interpolation(widths, segments, control_point_counts, is_cyclic); + interpolation = get_curve_width_interpolation( + widths, segments, control_point_counts, is_cyclic, reports); } static void populate_curve_verts_for_bezier(const bke::CurvesGeometry &geometry, @@ -246,7 +249,8 @@ static void populate_curve_props_for_bezier(const bke::CurvesGeometry &geometry, pxr::VtIntArray &control_point_counts, pxr::VtArray &widths, pxr::TfToken &interpolation, - const bool is_cyclic) + const bool is_cyclic, + ReportList *reports) { const int num_curves = geometry.curve_num; @@ -262,7 +266,8 @@ static void populate_curve_props_for_bezier(const bke::CurvesGeometry &geometry, geometry, positions, handles_l, handles_r, verts, control_point_counts, segments, is_cyclic); populate_curve_widths(geometry, widths); - interpolation = get_curve_width_interpolation(widths, segments, control_point_counts, is_cyclic); + interpolation = get_curve_width_interpolation( + widths, segments, control_point_counts, is_cyclic, reports); } static void populate_curve_props_for_nurbs(const bke::CurvesGeometry &geometry, @@ -397,7 +402,8 @@ void USDCurvesWriter::do_write(HierarchyContext &context) }); if (number_of_curve_types > 1) { - WM_report(RPT_WARNING, "Cannot export mixed curve types in the same Curves object"); + BKE_report( + reports(), RPT_WARNING, "Cannot export mixed curve types in the same Curves object"); return; } @@ -413,8 +419,9 @@ void USDCurvesWriter::do_write(HierarchyContext &context) } if (!all_same_cyclic_type) { - WM_report(RPT_WARNING, - "Cannot export mixed cyclic and non-cyclic curves in the same Curves object"); + BKE_report(reports(), + RPT_WARNING, + "Cannot export mixed cyclic and non-cyclic curves in the same Curves object"); return; } @@ -441,12 +448,13 @@ void USDCurvesWriter::do_write(HierarchyContext &context) RNA_enum_name_from_value( rna_enum_curves_type_items, int(curve_type), ¤t_curve_type_name); - WM_reportf(RPT_WARNING, - "USD does not support animating curve types. The curve type changes from %s to " - "%s on frame %f", - IFACE_(first_frame_curve_type_name), - IFACE_(current_curve_type_name), - timecode.GetValue()); + BKE_reportf(reports(), + RPT_WARNING, + "USD does not support animating curve types. The curve type changes from %s to " + "%s on frame %f", + IFACE_(first_frame_curve_type_name), + IFACE_(current_curve_type_name), + timecode.GetValue()); return; } @@ -454,22 +462,34 @@ void USDCurvesWriter::do_write(HierarchyContext &context) case CURVE_TYPE_POLY: usd_curves = DefineUsdGeomBasisCurves(pxr::VtValue(), is_cyclic, false); - populate_curve_props( - geometry, verts, control_point_counts, widths, interpolation, is_cyclic, false); + populate_curve_props(geometry, + verts, + control_point_counts, + widths, + interpolation, + is_cyclic, + false, + reports()); break; case CURVE_TYPE_CATMULL_ROM: usd_curves = DefineUsdGeomBasisCurves( pxr::VtValue(pxr::UsdGeomTokens->catmullRom), is_cyclic, true); - populate_curve_props( - geometry, verts, control_point_counts, widths, interpolation, is_cyclic, true); + populate_curve_props(geometry, + verts, + control_point_counts, + widths, + interpolation, + is_cyclic, + true, + reports()); break; case CURVE_TYPE_BEZIER: usd_curves = DefineUsdGeomBasisCurves( pxr::VtValue(pxr::UsdGeomTokens->bezier), is_cyclic, true); populate_curve_props_for_bezier( - geometry, verts, control_point_counts, widths, interpolation, is_cyclic); + geometry, verts, control_point_counts, widths, interpolation, is_cyclic, reports()); break; case CURVE_TYPE_NURBS: { pxr::VtArray knots; diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index 2b01eb0ffbf..dbff0be8fc4 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -13,6 +13,7 @@ #include "BKE_main.h" #include "BKE_node.hh" #include "BKE_node_runtime.hh" +#include "BKE_report.h" #include "IMB_colormanagement.h" @@ -428,7 +429,8 @@ static std::string get_in_memory_texture_filename(Image *ima) static void export_in_memory_texture(Image *ima, const std::string &export_dir, - const bool allow_overwrite) + const bool allow_overwrite, + ReportList *reports) { char image_abs_path[FILE_MAX]; @@ -472,7 +474,8 @@ static void export_in_memory_texture(Image *ima, 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); + BKE_reportf( + reports, RPT_WARNING, "USD export: couldn't export in-memory texture to %s", export_path); } } @@ -702,7 +705,8 @@ static std::string get_tex_image_asset_filepath(const USDExporterContext &usd_ex * destination directory. */ static void copy_tiled_textures(Image *ima, const std::string &dest_dir, - const bool allow_overwrite) + const bool allow_overwrite, + ReportList *reports) { char src_path[FILE_MAX]; get_absolute_path(ima, src_path); @@ -743,17 +747,21 @@ static void copy_tiled_textures(Image *ima, /* Copy the file. */ if (BLI_copy(src_tile_path, dest_tile_path) != 0) { - WM_reportf(RPT_WARNING, - "USD export: could not copy texture tile from %s to %s", - src_tile_path, - dest_tile_path); + BKE_reportf(reports, + RPT_WARNING, + "USD export: could not copy texture tile from %s to %s", + src_tile_path, + dest_tile_path); } } MEM_SAFE_FREE(udim_pattern); } /* Copy the given image to the destination directory. */ -static void copy_single_file(Image *ima, const std::string &dest_dir, const bool allow_overwrite) +static void copy_single_file(Image *ima, + const std::string &dest_dir, + const bool allow_overwrite, + ReportList *reports) { char source_path[FILE_MAX]; get_absolute_path(ima, source_path); @@ -777,8 +785,11 @@ static void copy_single_file(Image *ima, const std::string &dest_dir, const bool /* Copy the file. */ if (BLI_copy(source_path, dest_path) != 0) { - WM_reportf( - RPT_WARNING, "USD export: could not copy texture from %s to %s", source_path, dest_path); + BKE_reportf(reports, + RPT_WARNING, + "USD export: could not copy texture from %s to %s", + source_path, + dest_path); } } @@ -816,13 +827,16 @@ static void export_texture(const USDExporterContext &usd_export_context, bNode * std::string dest_dir(tex_dir_path); if (is_generated || is_dirty || is_packed) { - export_in_memory_texture(ima, dest_dir, allow_overwrite); + export_in_memory_texture( + ima, dest_dir, allow_overwrite, usd_export_context.export_params.worker_status->reports); } else if (ima->source == IMA_SRC_TILED) { - copy_tiled_textures(ima, dest_dir, allow_overwrite); + copy_tiled_textures( + ima, dest_dir, allow_overwrite, usd_export_context.export_params.worker_status->reports); } else { - copy_single_file(ima, dest_dir, allow_overwrite); + copy_single_file( + ima, dest_dir, allow_overwrite, usd_export_context.export_params.worker_status->reports); } } @@ -853,7 +867,10 @@ pxr::UsdShadeMaterial create_usd_material(const USDExporterContext &usd_export_c create_usd_viewport_material(usd_export_context, material, usd_material); } - call_material_export_hooks(usd_export_context.stage, material, usd_material); + call_material_export_hooks(usd_export_context.stage, + material, + usd_material, + usd_export_context.export_params.worker_status->reports); return usd_material; } diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index 2b30a68a3bd..53d98944246 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -24,6 +24,7 @@ #include "BKE_mesh_wrapper.hh" #include "BKE_modifier.h" #include "BKE_object.hh" +#include "BKE_report.h" #include "DEG_depsgraph.hh" @@ -128,7 +129,7 @@ void USDGenericMeshWriter::write_custom_data(const Mesh *mesh, pxr::UsdGeomMesh } static std::optional convert_blender_type_to_usd( - const eCustomDataType blender_type) + const eCustomDataType blender_type, ReportList *reports) { switch (blender_type) { case CD_PROP_FLOAT: @@ -147,13 +148,13 @@ static std::optional convert_blender_type_to_usd( case CD_PROP_QUATERNION: return pxr::SdfValueTypeNames->QuatfArray; default: - WM_reportf(RPT_WARNING, "Unsupported type for mesh data"); + BKE_reportf(reports, RPT_WARNING, "Unsupported type for mesh data"); return std::nullopt; } } static const std::optional convert_blender_domain_to_usd( - const eAttrDomain blender_domain) + const eAttrDomain blender_domain, ReportList *reports) { switch (blender_domain) { case ATTR_DOMAIN_CORNER: @@ -165,7 +166,7 @@ static const std::optional convert_blender_domain_to_usd( /* Notice: Edge types are not supported in USD! */ default: - WM_reportf(RPT_WARNING, "Unsupported type for mesh data"); + BKE_reportf(reports, RPT_WARNING, "Unsupported type for mesh data"); return std::nullopt; } } @@ -231,9 +232,10 @@ void USDGenericMeshWriter::write_generic_data(const Mesh *mesh, const pxr::UsdGeomPrimvarsAPI pvApi = pxr::UsdGeomPrimvarsAPI(usd_mesh); /* Varying type depends on original domain. */ - const std::optional prim_varying = convert_blender_domain_to_usd(meta_data.domain); + const std::optional prim_varying = convert_blender_domain_to_usd(meta_data.domain, + reports()); const std::optional prim_attr_type = convert_blender_type_to_usd( - meta_data.data_type); + meta_data.data_type, reports()); const GVArraySpan attribute = *mesh->attributes().lookup( attribute_id, meta_data.domain, meta_data.data_type); @@ -242,10 +244,11 @@ void USDGenericMeshWriter::write_generic_data(const Mesh *mesh, } if (!prim_varying || !prim_attr_type) { - WM_reportf(RPT_WARNING, - "Mesh %s, Attribute %s cannot be converted to USD", - &mesh->id.name[2], - attribute_id.name().data()); + BKE_reportf(reports(), + RPT_WARNING, + "Mesh %s, Attribute %s cannot be converted to USD", + &mesh->id.name[2], + attribute_id.name().data()); return; } diff --git a/source/blender/io/usd/intern/usd_writer_volume.cc b/source/blender/io/usd/intern/usd_writer_volume.cc index 784f61e81e5..8d85f924735 100644 --- a/source/blender/io/usd/intern/usd_writer_volume.cc +++ b/source/blender/io/usd/intern/usd_writer_volume.cc @@ -11,6 +11,7 @@ #include "DNA_volume_types.h" #include "DNA_windowmanager_types.h" +#include "BKE_report.h" #include "BKE_volume.h" #include "BLI_fileops.h" @@ -47,9 +48,10 @@ void USDVolumeWriter::do_write(HierarchyContext &context) auto vdb_file_path = resolve_vdb_file(volume); if (!vdb_file_path.has_value()) { - WM_reportf(RPT_WARNING, - "USD Export: failed to resolve .vdb file for object: %s", - volume->id.name + 2); + BKE_reportf(reports(), + RPT_WARNING, + "USD Export: failed to resolve .vdb file for object: %s", + volume->id.name + 2); return; } @@ -58,9 +60,10 @@ void USDVolumeWriter::do_write(HierarchyContext &context) vdb_file_path = relative_vdb_file_path; } else { - WM_reportf(RPT_WARNING, - "USD Export: couldn't construct relative file path for .vdb file, absolute path " - "will be used instead"); + BKE_reportf(reports(), + RPT_WARNING, + "USD Export: couldn't construct relative file path for .vdb file, absolute path " + "will be used instead"); } } diff --git a/source/blender/io/usd/tests/usd_curves_test.cc b/source/blender/io/usd/tests/usd_curves_test.cc index ccdcc97681f..1a1aab8cdd6 100644 --- a/source/blender/io/usd/tests/usd_curves_test.cc +++ b/source/blender/io/usd/tests/usd_curves_test.cc @@ -110,7 +110,7 @@ TEST_F(UsdCurvesTest, usd_export_curves) USDExportParams params; - const bool result = USD_export(context, output_filename.c_str(), ¶ms, false); + const bool result = USD_export(context, output_filename.c_str(), ¶ms, false, nullptr); EXPECT_TRUE(result) << "USD export should succed."; pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(output_filename); diff --git a/source/blender/io/usd/tests/usd_export_test.cc b/source/blender/io/usd/tests/usd_export_test.cc index 90fa3b366f4..dd93ae2a4b5 100644 --- a/source/blender/io/usd/tests/usd_export_test.cc +++ b/source/blender/io/usd/tests/usd_export_test.cc @@ -214,7 +214,7 @@ TEST_F(UsdExportTest, usd_export_rain_mesh) params.export_uvmaps = false; params.visible_objects_only = true; - bool result = USD_export(context, output_filename.c_str(), ¶ms, false); + bool result = USD_export(context, output_filename.c_str(), ¶ms, false, nullptr); ASSERT_TRUE(result) << "Writing to " << output_filename << " failed!"; pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(output_filename); @@ -280,7 +280,7 @@ TEST_F(UsdExportTest, usd_export_material) params.generate_preview_surface = true; params.relative_paths = false; - const bool result = USD_export(context, output_filename.c_str(), ¶ms, false); + const bool result = USD_export(context, output_filename.c_str(), ¶ms, false, nullptr); ASSERT_TRUE(result) << "Unable to export stage to " << output_filename; pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(output_filename); diff --git a/source/blender/io/usd/tests/usd_usdz_export_test.cc b/source/blender/io/usd/tests/usd_usdz_export_test.cc index d04eaae046f..d36ecf9c5f3 100644 --- a/source/blender/io/usd/tests/usd_usdz_export_test.cc +++ b/source/blender/io/usd/tests/usd_usdz_export_test.cc @@ -98,7 +98,7 @@ TEST_F(UsdUsdzExportTest, usdz_export) params.export_materials = false; params.visible_objects_only = false; - bool result = USD_export(context, output_filepath, ¶ms, false); + bool result = USD_export(context, output_filepath, ¶ms, false, nullptr); ASSERT_TRUE(result) << "usd export to " << output_filepath << " failed."; pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(output_filepath); diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 5908bdfae26..3b40d86cfb7 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -16,6 +16,7 @@ struct CacheArchiveHandle; struct CacheReader; struct Object; struct bContext; +struct wmJobWorkerStatus; /* Behavior when the name of an imported material * conflicts with an existing material. */ @@ -55,6 +56,10 @@ struct USDExportParams { bool overwrite_textures = true; bool relative_paths = true; char root_prim_path[1024] = ""; /* FILE_MAX */ + + /** Communication structure between the wmJob management code and the worker code. Currently used + * to generate safely reports from the worker thread. */ + wmJobWorkerStatus *worker_status; }; struct USDImportParams { @@ -91,6 +96,10 @@ struct USDImportParams { char import_textures_dir[768]; /* FILE_MAXDIR */ eUSDTexNameCollisionMode tex_name_collision_mode; bool import_all_materials; + + /** Communication structure between the wmJob management code and the worker code. Currently used + * to generate safely reports from the worker thread. */ + wmJobWorkerStatus *worker_status; }; /* This struct is in place to store the mesh sequence parameters needed when reading a data from a @@ -115,12 +124,14 @@ USDMeshReadParams create_mesh_read_params(double motion_sample_time, int read_fl bool USD_export(struct bContext *C, const char *filepath, const struct USDExportParams *params, - bool as_background_job); + bool as_background_job, + ReportList *reports); bool USD_import(struct bContext *C, const char *filepath, const struct USDImportParams *params, - bool as_background_job); + bool as_background_job, + ReportList *reports); int USD_get_version(void); diff --git a/tests/python/bl_usd_import_test.py b/tests/python/bl_usd_import_test.py index ad3fbd31ec0..595600a7eb8 100644 --- a/tests/python/bl_usd_import_test.py +++ b/tests/python/bl_usd_import_test.py @@ -46,8 +46,12 @@ class USDImportTest(AbstractUSDTest): self.assertEqual({'FINISHED'}, res, f"Unable to import USD file {infile}") infile = str(self.testdir / "this_file_doesn't_exist.usda") - res = bpy.ops.wm.usd_import(filepath=infile) - self.assertEqual({'CANCELLED'}, res, "Was somehow able to import a non-existent USD file!") + # RPT_ERROR Reports from operators generate `RuntimeError` python exceptions. + try: + res = bpy.ops.wm.usd_import(filepath=infile) + self.assertEqual({'CANCELLED'}, res, "Was somehow able to import a non-existent USD file!") + except RuntimeError as e: + self.assertTrue(e.args[0].startswith("Error: USD Import: unable to open stage to read")) def test_import_prim_hierarchy(self): """Test importing a simple object hierarchy from a USDA file."""