diff options
Diffstat (limited to 'source/blender/io/usd/intern/usd_capi_export.cc')
-rw-r--r-- | source/blender/io/usd/intern/usd_capi_export.cc | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc new file mode 100644 index 00000000000..25f12e683cf --- /dev/null +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -0,0 +1,238 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ + +#include "usd.h" +#include "usd_common.h" +#include "usd_hierarchy_iterator.h" + +#include <pxr/base/plug/registry.h> +#include <pxr/pxr.h> +#include <pxr/usd/usd/stage.h> +#include <pxr/usd/usdGeom/tokens.h> + +#include "MEM_guardedalloc.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "DNA_scene_types.h" + +#include "BKE_appdir.h" +#include "BKE_blender_version.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_scene.h" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" +#include "WM_types.h" + +namespace blender::io::usd { + +struct ExportJobData { + Main *bmain; + Depsgraph *depsgraph; + wmWindowManager *wm; + + char filename[FILE_MAX]; + USDExportParams params; + + bool export_ok; +}; + +static void export_startjob(void *customdata, + /* Cannot be const, this function implements wm_jobs_start_callback. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + short *stop, + short *do_update, + float *progress) +{ + ExportJobData *data = static_cast<ExportJobData *>(customdata); + data->export_ok = false; + + G.is_rendering = true; + WM_set_locked_interface(data->wm, true); + G.is_break = false; + + /* Construct the depsgraph for exporting. */ + Scene *scene = DEG_get_input_scene(data->depsgraph); + if (data->params.visible_objects_only) { + DEG_graph_build_from_view_layer(data->depsgraph); + } + else { + DEG_graph_build_for_all_objects(data->depsgraph); + } + BKE_scene_graph_update_tagged(data->depsgraph, data->bmain); + + *progress = 0.0f; + *do_update = true; + + /* For restoring the current frame after exporting animation is done. */ + const int orig_frame = CFRA; + + pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filename); + 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->filename); + return; + } + + usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z)); + usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit, + pxr::VtValue(scene->unit.scale_length)); + usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender v") + + BKE_blender_version_string()); + + /* Set up the stage for animated data. */ + if (data->params.export_animation) { + usd_stage->SetTimeCodesPerSecond(FPS); + usd_stage->SetStartTimeCode(scene->r.sfra); + usd_stage->SetEndTimeCode(scene->r.efra); + } + + USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params); + + if (data->params.export_animation) { + /* Writing the animated frames is not 100% of the work, but it's our best guess. */ + float progress_per_frame = 1.0f / std::max(1, (scene->r.efra - scene->r.sfra + 1)); + + for (float frame = scene->r.sfra; frame <= scene->r.efra; frame++) { + if (G.is_break || (stop != nullptr && *stop)) { + break; + } + + /* Update the scene for the next frame to render. */ + scene->r.cfra = static_cast<int>(frame); + scene->r.subframe = frame - scene->r.cfra; + BKE_scene_graph_update_for_newframe(data->depsgraph); + + iter.set_export_frame(frame); + iter.iterate_and_write(); + + *progress += progress_per_frame; + *do_update = true; + } + } + else { + /* If we're not animating, a single iteration over all objects is enough. */ + iter.iterate_and_write(); + } + + iter.release_writers(); + usd_stage->GetRootLayer()->Save(); + + /* Finish up by going back to the keyframe that was current before we started. */ + if (CFRA != orig_frame) { + CFRA = orig_frame; + BKE_scene_graph_update_for_newframe(data->depsgraph); + } + + data->export_ok = true; + *progress = 1.0f; + *do_update = true; +} + +static void export_endjob(void *customdata) +{ + ExportJobData *data = static_cast<ExportJobData *>(customdata); + + DEG_graph_free(data->depsgraph); + + if (!data->export_ok && BLI_exists(data->filename)) { + BLI_delete(data->filename, false, false); + } + + G.is_rendering = false; + WM_set_locked_interface(data->wm, false); +} + +} // namespace blender::io::usd + +bool USD_export(bContext *C, + const char *filepath, + const USDExportParams *params, + bool as_background_job) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + Scene *scene = CTX_data_scene(C); + + blender::io::usd::ensure_usd_plugin_path_registered(); + + blender::io::usd::ExportJobData *job = static_cast<blender::io::usd::ExportJobData *>( + MEM_mallocN(sizeof(blender::io::usd::ExportJobData), "ExportJobData")); + + job->bmain = CTX_data_main(C); + job->wm = CTX_wm_manager(C); + job->export_ok = false; + BLI_strncpy(job->filename, filepath, sizeof(job->filename)); + + job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode); + job->params = *params; + + bool export_ok = false; + if (as_background_job) { + wmJob *wm_job = WM_jobs_get( + job->wm, CTX_wm_window(C), scene, "USD Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, MEM_freeN); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, + blender::io::usd::export_startjob, + nullptr, + nullptr, + blender::io::usd::export_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ + short stop = 0, do_update = 0; + float progress = 0.0f; + + blender::io::usd::export_startjob(job, &stop, &do_update, &progress); + blender::io::usd::export_endjob(job); + export_ok = job->export_ok; + + MEM_freeN(job); + } + + return export_ok; +} + +int USD_get_version(void) +{ + /* USD 19.11 defines: + * + * #define PXR_MAJOR_VERSION 0 + * #define PXR_MINOR_VERSION 19 + * #define PXR_PATCH_VERSION 11 + * #define PXR_VERSION 1911 + * + * So the major version is implicit/invisible in the public version number. + */ + return PXR_VERSION; +} |