Blender V5.0
abc_export_capi.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "ABC_alembic.h"
7#include "abc_archive.h"
9
10#include "MEM_guardedalloc.h"
11
12#include "DEG_depsgraph.hh"
15
16#include "DNA_scene_types.h"
17
18#include "BKE_context.hh"
19#include "BKE_global.hh"
20#include "BKE_lib_id.hh"
21#include "BKE_main.hh"
22#include "BKE_scene.hh"
23
24#include "BLI_fileops.h"
25#include "BLI_path_utils.hh"
26#include "BLI_string.h"
27#include "BLI_timeit.hh"
28
29#include "WM_api.hh"
30#include "WM_types.hh"
31
32#include "CLG_log.h"
33static CLG_LogRef LOG = {"io.alembic"};
34
35#include <memory>
36
38 Main *bmain = nullptr;
39 Depsgraph *depsgraph = nullptr;
40 wmWindowManager *wm = nullptr;
41
42 char filepath[FILE_MAX] = {};
44
45 bool was_canceled = false;
46 bool export_ok = false;
48};
49
50namespace blender::io::alembic {
51
52/* Construct the depsgraph for exporting. */
54{
55 if (job->params.collection[0]) {
56 Collection *collection = reinterpret_cast<Collection *>(
58 if (!collection) {
60 RPT_ERROR, "Alembic Export: Unable to find collection '%s'", job->params.collection);
61 return false;
62 }
63
65 }
66 else {
68 }
69
70 return true;
71}
72
74{
75 blender::timeit::Nanoseconds duration = blender::timeit::Clock::now() - data->start_time;
76 std::cout << "Alembic export of '" << data->filepath << "' took ";
78 std::cout << '\n';
79}
80
81static void export_startjob(void *customdata, wmJobWorkerStatus *worker_status)
82{
83 ExportJobData *data = static_cast<ExportJobData *>(customdata);
84 data->was_canceled = false;
85 data->start_time = blender::timeit::Clock::now();
86
87 G.is_rendering = true;
89 G.is_break = false;
90
91 worker_status->progress = 0.0f;
92 worker_status->do_update = true;
93
94 BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
95
96 SubdivModifierDisabler subdiv_disabler(data->depsgraph);
97 if (!data->params.apply_subdiv) {
98 subdiv_disabler.disable_modifiers();
99 BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
100 }
101
102 /* For restoring the current frame after exporting animation is done. */
103 Scene *scene = DEG_get_input_scene(data->depsgraph);
104 const int orig_frame = scene->r.cfra;
105 const bool export_animation = (data->params.frame_start != data->params.frame_end);
106
107 /* Create the Alembic archive. */
108 std::unique_ptr<ABCArchive> abc_archive;
109 try {
110 abc_archive = std::make_unique<ABCArchive>(
111 data->bmain, scene, data->params, std::string(data->filepath));
112 }
113 catch (const std::exception &ex) {
114 std::stringstream error_message_stream;
115 error_message_stream << "Error writing to " << data->filepath;
116 const std::string &error_message = error_message_stream.str();
117
118 /* The exception message can be very cryptic (just "iostream error" on Linux, for example),
119 * so better not to include it in the report. */
120 CLOG_ERROR(&LOG, "%s: %s", error_message.c_str(), ex.what());
121 WM_global_report(RPT_ERROR, error_message.c_str());
122 data->export_ok = false;
123 return;
124 }
125 catch (...) {
126 /* Unknown exception class, so we cannot include its message. */
127 std::stringstream error_message_stream;
128 error_message_stream << "Unknown error writing to " << data->filepath;
129 WM_global_report(RPT_ERROR, error_message_stream.str().c_str());
130 data->export_ok = false;
131 return;
132 }
133
134 ABCHierarchyIterator iter(data->bmain, data->depsgraph, abc_archive.get(), data->params);
135
136 if (export_animation) {
137 CLOG_STR_DEBUG(&LOG, "Exporting animation");
138
139 /* Writing the animated frames is not 100% of the work, but it's our best guess. */
140 const float progress_per_frame = 1.0f / std::max(size_t(1), abc_archive->total_frame_count());
141 ABCArchive::Frames::const_iterator frame_it = abc_archive->frames_begin();
142 const ABCArchive::Frames::const_iterator frames_end = abc_archive->frames_end();
143
144 for (; frame_it != frames_end; frame_it++) {
145 double frame = *frame_it;
146
147 if (G.is_break || worker_status->stop) {
148 break;
149 }
150
151 /* Update the scene for the next frame to render. */
152 scene->r.cfra = int(frame);
153 scene->r.subframe = float(frame - scene->r.cfra);
155
156 CLOG_DEBUG(&LOG, "Exporting frame %.2f", frame);
157 ExportSubset export_subset = abc_archive->export_subset_for_frame(frame);
158 iter.set_export_subset(export_subset);
159 iter.iterate_and_write();
160
161 worker_status->progress += progress_per_frame;
162 worker_status->do_update = true;
163 }
164 }
165 else {
166 /* If we're not animating, a single iteration over all objects is enough. */
167 iter.iterate_and_write();
168 }
169
170 iter.release_writers();
171
172 /* Finish up by going back to the keyframe that was current before we started. */
173 if (scene->r.cfra != orig_frame) {
174 scene->r.cfra = orig_frame;
176 }
177
178 data->export_ok = !data->was_canceled;
179
180 worker_status->progress = 1.0f;
181 worker_status->do_update = true;
182}
183
184static void export_endjob(void *customdata)
185{
186 ExportJobData *data = static_cast<ExportJobData *>(customdata);
187
188 DEG_graph_free(data->depsgraph);
189
190 if (data->was_canceled && BLI_exists(data->filepath)) {
191 BLI_delete(data->filepath, false, false);
192 }
193
194 G.is_rendering = false;
195 WM_locked_interface_set(data->wm, false);
197}
198
199} // namespace blender::io::alembic
200
201bool ABC_export(Scene *scene,
202 bContext *C,
203 const char *filepath,
205 bool as_background_job)
206{
207 ViewLayer *view_layer = CTX_data_view_layer(C);
208
209 ExportJobData *job = MEM_new<ExportJobData>("ExportJobData");
210
211 job->bmain = CTX_data_main(C);
212 job->wm = CTX_wm_manager(C);
213 job->export_ok = false;
214 STRNCPY(job->filepath, filepath);
215
216 job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode);
217 job->params = *params;
218
219 /* Construct the depsgraph for exporting.
220 *
221 * Has to be done from main thread currently, as it may affect Main original data (e.g. when
222 * doing deferred update of the view-layers, see #112534 for details). */
224 return false;
225 }
226
227 bool export_ok = false;
228 if (as_background_job) {
229 wmJob *wm_job = WM_jobs_get(job->wm,
231 scene,
232 "Exporting Alembic...",
235
236 /* setup job */
238 wm_job, job, [](void *j) { MEM_delete(static_cast<ExportJobData *>(j)); });
240 WM_jobs_callbacks(wm_job,
242 nullptr,
243 nullptr,
245
247 }
248 else {
249 wmJobWorkerStatus worker_status = {};
250 blender::io::alembic::export_startjob(job, &worker_status);
252 export_ok = job->export_ok;
253
254 MEM_delete(job);
255 }
256
257 return export_ok;
258}
wmWindow * CTX_wm_window(const bContext *C)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
ID * BKE_libblock_find_name(Main *bmain, short type, const char *name, const std::optional< Library * > lib=std::nullopt) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition lib_id.cc:1710
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2621
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2700
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:360
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
#define FILE_MAX
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
#define CLOG_DEBUG(clg_ref,...)
Definition CLG_log.h:191
#define CLOG_STR_DEBUG(clg_ref, str)
Definition CLG_log.h:199
Depsgraph * DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
Definition depsgraph.cc:278
void DEG_graph_free(Depsgraph *graph)
Definition depsgraph.cc:306
void DEG_graph_build_from_collection(Depsgraph *graph, Collection *collection)
void DEG_graph_build_from_view_layer(Depsgraph *graph)
Scene * DEG_get_input_scene(const Depsgraph *graph)
@ ID_GR
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_ALEMBIC_EXPORT
Definition WM_api.hh:1797
@ WM_JOB_PROGRESS
Definition WM_api.hh:1766
#define NC_SCENE
Definition WM_types.hh:378
#define ND_FRAME
Definition WM_types.hh:434
bool ABC_export(Scene *scene, bContext *C, const char *filepath, const AlembicExportParams *params, bool as_background_job)
BMesh const char void * data
void set_export_subset(ExportSubset export_subset)
nullptr float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define LOG(level)
Definition log.h:97
#define G(x, y, z)
static void export_endjob(void *customdata)
static void export_startjob(void *customdata, wmJobWorkerStatus *worker_status)
static bool build_depsgraph(ExportJobData *job)
static void report_job_duration(const ExportJobData *data)
std::chrono::nanoseconds Nanoseconds
Definition BLI_timeit.hh:21
Clock::time_point TimePoint
Definition BLI_timeit.hh:20
void print_duration(Nanoseconds duration)
Definition timeit.cc:45
char collection[MAX_ID_NAME - 2]
Definition ABC_alembic.h:62
Depsgraph * depsgraph
char filepath[FILE_MAX]
wmWindowManager * wm
AlembicExportParams params
blender::timeit::TimePoint start_time
struct RenderData r
void WM_locked_interface_set(wmWindowManager *wm, bool lock)
void WM_global_report(eReportType type, const char *message)
void WM_global_reportf(eReportType type, const char *format,...)
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:376
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:479
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:211
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:388
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:360