/* * 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 #include #include #include #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(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, static_cast(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(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(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( 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() { /* 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; }