Changeset View
Standalone View
source/blender/usd/intern/usd_capi.cc
- This file was added.
| /* | |||||
| * 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_hierarchy_iterator.h" | |||||
| #include <pxr/usd/usd/stage.h> | |||||
| #include <pxr/usd/usdGeom/tokens.h> | |||||
| extern "C" { | |||||
| #include "DEG_depsgraph.h" | |||||
| #include "DEG_depsgraph_build.h" | |||||
| #include "DEG_depsgraph_query.h" | |||||
| #include "DNA_scene_types.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 "MEM_guardedalloc.h" | |||||
| #include "WM_api.h" | |||||
| #include "WM_types.h" | |||||
| } | |||||
| struct ExportJobData { | |||||
| ViewLayer *view_layer; | |||||
| Main *bmain; | |||||
| Depsgraph *depsgraph; | |||||
| wmWindowManager *wm; | |||||
| char filename[FILE_MAX]; | |||||
sergey: `FILE_MAX` | |||||
| USDExportParams params; | |||||
| short *stop; | |||||
| short *do_update; | |||||
| float *progress; | |||||
| bool was_canceled; | |||||
| bool export_ok; | |||||
| }; | |||||
| static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) | |||||
| { | |||||
| ExportJobData *data = static_cast<ExportJobData *>(customdata); | |||||
| data->stop = stop; | |||||
| data->do_update = do_update; | |||||
| data->progress = progress; | |||||
| data->was_canceled = 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); | |||||
| ViewLayer *view_layer = DEG_get_input_view_layer(data->depsgraph); | |||||
| DEG_graph_build_from_view_layer(data->depsgraph, data->bmain, scene, view_layer); | |||||
| BKE_scene_graph_update_tagged(data->depsgraph, data->bmain); | |||||
| // Constructing & evaluating the depsgraph counts as 10% of the work. | |||||
| *progress = 0.1f; | |||||
| *do_update = true; | |||||
| // For restoring the current frame after exporting animation is done. | |||||
| const int orig_frame = CFRA; | |||||
| // Create a stage and set up the metadata. | |||||
| pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filename); | |||||
| usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z)); | |||||
Done Inline ActionsProbably worth noting; there are a couple reasons stage creation could fail. TfErrorMark and UsdUtilsCoalescingDiagnosticDelegate can be used to capture errors and warnings respectively to relay to users. superfunc: Probably worth noting; there are a couple reasons stage creation could fail. `TfErrorMark` and… | |||||
Done Inline ActionsI'll leave that for later, I've added a note about this in T72204: Universal Scene Description sybren: I'll leave that for later, I've added a note about this in {T72204} | |||||
| usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit, | |||||
| pxr::VtValue(scene->unit.scale_length)); | |||||
| usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender ") + versionstr); | |||||
| // 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 80% of the work. | |||||
| float progress_per_frame = 0.8f / 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, data->bmain); | |||||
| iter.set_export_frame(frame); | |||||
| iter.iterate_and_write(); | |||||
Done Inline ActionsShould it it be iterate_and_write ? sergey: Should it it be `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(); | |||||
| // Writing the final file is the other 10% of the work. | |||||
| *progress = 0.9f; | |||||
Not Done Inline ActionsNot sure such magic constants add real benefit to user, but adds WTF factor when reading the code. Like, imagine you export 800 frames. You have nice continuous increase in the progress bar. Then frames are iterated and progress jumps from 80% to 90%, stays there for a bit. jumps to something which is close enough to 100% and then almost immediately finishes. And then all the timings are totally scene-dependent and wrong anyway. sergey: Not sure such magic constants add real benefit to user, but adds WTF factor when reading the… | |||||
Done Inline ActionsWell, the alternative is either waiting at 80% until it's done (so no progress between 'saving individual frames' and 'closing the file and finishing up', and 'moving to the pre-export current frame'), which doesn't help the user either. Or just go to 100% and let it wait there, which will be frustrating (100% should be 'done', and not 'almost done'). Of course it's not an accurate way to measure the progress, but given that we don't know in advance how complex each step is going to be (there could be a 100000 mesh particle system that only lives at frame 143) it's all approximation anyway. sybren: Well, the alternative is either waiting at 80% until it's done (so no progress between 'saving… | |||||
Not Done Inline ActionsI don't think such guesstimate makes things any more clear, and i don't think we should be adding it. sergey: I don't think such guesstimate makes things any more clear, and i don't think we should be… | |||||
Done Inline ActionsWhat's the alternative? No progress report at all, simply because it can't be perfect? sybren: What's the alternative? No progress report at all, simply because it can't be perfect? | |||||
Done Inline ActionsJust set progress based on current frame and frame range, without attempts to try be more monotonous. In a more ideal world i would think job system should be able to report status of steps which don't have clear progress. sergey: Just set progress based on current frame and frame range, without attempts to try be more… | |||||
| *do_update = true; | |||||
| usd_stage->GetRootLayer()->Save(); | |||||
| // Finish up by going back to the keyframe that was current before we started. | |||||
| *progress = 0.99f; | |||||
| *do_update = true; | |||||
| if (CFRA != orig_frame) { | |||||
| CFRA = orig_frame; | |||||
| BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); | |||||
| } | |||||
| data->export_ok = !data->was_canceled; | |||||
| *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->was_canceled && BLI_exists(data->filename)) { | |||||
| BLI_delete(data->filename, false, false); | |||||
| } | |||||
| G.is_rendering = false; | |||||
| WM_set_locked_interface(data->wm, false); | |||||
| } | |||||
| 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); | |||||
| ExportJobData *job = static_cast<ExportJobData *>( | |||||
| MEM_mallocN(sizeof(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)); | |||||
Done Inline Actionssizeof(job->filename). sergey: `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, export_startjob, NULL, NULL, 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.f; | |||||
| export_startjob(job, &stop, &do_update, &progress); | |||||
| export_endjob(job); | |||||
| export_ok = job->export_ok; | |||||
| MEM_freeN(job); | |||||
| } | |||||
| return export_ok; | |||||
| } | |||||
FILE_MAX