Blender V4.3
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
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 if (job->params.visible_objects_only) {
68 }
69 else {
71 }
72
73 return true;
74}
75
76static void report_job_duration(const ExportJobData *data)
77{
78 blender::timeit::Nanoseconds duration = blender::timeit::Clock::now() - data->start_time;
79 std::cout << "Alembic export of '" << data->filepath << "' took ";
81 std::cout << '\n';
82}
83
84static void export_startjob(void *customdata, wmJobWorkerStatus *worker_status)
85{
86 ExportJobData *data = static_cast<ExportJobData *>(customdata);
87 data->was_canceled = false;
88 data->start_time = blender::timeit::Clock::now();
89
90 G.is_rendering = true;
91 WM_set_locked_interface(data->wm, true);
92 G.is_break = false;
93
94 worker_status->progress = 0.0f;
95 worker_status->do_update = true;
96
97 BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
98
99 SubdivModifierDisabler subdiv_disabler(data->depsgraph);
100 if (!data->params.apply_subdiv) {
101 subdiv_disabler.disable_modifiers();
102 BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
103 }
104
105 /* For restoring the current frame after exporting animation is done. */
106 Scene *scene = DEG_get_input_scene(data->depsgraph);
107 const int orig_frame = scene->r.cfra;
108 const bool export_animation = (data->params.frame_start != data->params.frame_end);
109
110 /* Create the Alembic archive. */
111 std::unique_ptr<ABCArchive> abc_archive;
112 try {
113 abc_archive = std::make_unique<ABCArchive>(
114 data->bmain, scene, data->params, std::string(data->filepath));
115 }
116 catch (const std::exception &ex) {
117 std::stringstream error_message_stream;
118 error_message_stream << "Error writing to " << data->filepath;
119 const std::string &error_message = error_message_stream.str();
120
121 /* The exception message can be very cryptic (just "iostream error" on Linux, for example),
122 * so better not to include it in the report. */
123 CLOG_ERROR(&LOG, "%s: %s", error_message.c_str(), ex.what());
124 WM_report(RPT_ERROR, error_message.c_str());
125 data->export_ok = false;
126 return;
127 }
128 catch (...) {
129 /* Unknown exception class, so we cannot include its message. */
130 std::stringstream error_message_stream;
131 error_message_stream << "Unknown error writing to " << data->filepath;
132 WM_report(RPT_ERROR, error_message_stream.str().c_str());
133 data->export_ok = false;
134 return;
135 }
136
137 ABCHierarchyIterator iter(data->bmain, data->depsgraph, abc_archive.get(), data->params);
138
139 if (export_animation) {
140 CLOG_INFO(&LOG, 2, "Exporting animation");
141
142 /* Writing the animated frames is not 100% of the work, but it's our best guess. */
143 const float progress_per_frame = 1.0f / std::max(size_t(1), abc_archive->total_frame_count());
144 ABCArchive::Frames::const_iterator frame_it = abc_archive->frames_begin();
145 const ABCArchive::Frames::const_iterator frames_end = abc_archive->frames_end();
146
147 for (; frame_it != frames_end; frame_it++) {
148 double frame = *frame_it;
149
150 if (G.is_break || worker_status->stop) {
151 break;
152 }
153
154 /* Update the scene for the next frame to render. */
155 scene->r.cfra = int(frame);
156 scene->r.subframe = float(frame - scene->r.cfra);
158
159 CLOG_INFO(&LOG, 2, "Exporting frame %.2f", frame);
160 ExportSubset export_subset = abc_archive->export_subset_for_frame(frame);
161 iter.set_export_subset(export_subset);
162 iter.iterate_and_write();
163
164 worker_status->progress += progress_per_frame;
165 worker_status->do_update = true;
166 }
167 }
168 else {
169 /* If we're not animating, a single iteration over all objects is enough. */
170 iter.iterate_and_write();
171 }
172
173 iter.release_writers();
174
175 /* Finish up by going back to the keyframe that was current before we started. */
176 if (scene->r.cfra != orig_frame) {
177 scene->r.cfra = orig_frame;
179 }
180
181 data->export_ok = !data->was_canceled;
182
183 worker_status->progress = 1.0f;
184 worker_status->do_update = true;
185}
186
187static void export_endjob(void *customdata)
188{
189 ExportJobData *data = static_cast<ExportJobData *>(customdata);
190
191 DEG_graph_free(data->depsgraph);
192
193 if (data->was_canceled && BLI_exists(data->filepath)) {
194 BLI_delete(data->filepath, false, false);
195 }
196
197 G.is_rendering = false;
198 WM_set_locked_interface(data->wm, false);
200}
201
202} // namespace blender::io::alembic
203
204bool ABC_export(Scene *scene,
205 bContext *C,
206 const char *filepath,
208 bool as_background_job)
209{
210 ViewLayer *view_layer = CTX_data_view_layer(C);
211
212 ExportJobData *job = static_cast<ExportJobData *>(
213 MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
214
215 job->bmain = CTX_data_main(C);
216 job->wm = CTX_wm_manager(C);
217 job->export_ok = false;
218 STRNCPY(job->filepath, filepath);
219
220 job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode);
221 job->params = *params;
222
223 /* Construct the depsgraph for exporting.
224 *
225 * Has to be done from main thread currently, as it may affect Main original data (e.g. when
226 * doing deferred update of the view-layers, see #112534 for details). */
228 return false;
229 }
230
231 bool export_ok = false;
232 if (as_background_job) {
233 wmJob *wm_job = WM_jobs_get(job->wm,
234 CTX_wm_window(C),
235 scene,
236 "Alembic Export",
239
240 /* setup job */
241 WM_jobs_customdata_set(wm_job, job, MEM_freeN);
243 WM_jobs_callbacks(wm_job,
245 nullptr,
246 nullptr,
248
249 WM_jobs_start(CTX_wm_manager(C), wm_job);
250 }
251 else {
252 wmJobWorkerStatus worker_status = {};
253 blender::io::alembic::export_startjob(job, &worker_status);
255 export_ok = job->export_ok;
256
257 MEM_freeN(job);
258 }
259
260 return export_ok;
261}
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:1657
void BKE_scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2568
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2647
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
#define FILE_MAX
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:182
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
Depsgraph * DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
Definition depsgraph.cc:273
void DEG_graph_free(Depsgraph *graph)
Definition depsgraph.cc:301
void DEG_graph_build_from_collection(Depsgraph *graph, Collection *collection)
void DEG_graph_build_for_all_objects(Depsgraph *graph)
void DEG_graph_build_from_view_layer(Depsgraph *graph)
Scene * DEG_get_input_scene(const Depsgraph *graph)
@ ID_GR
Read Guarded memory(de)allocation.
@ WM_JOB_TYPE_ALEMBIC_EXPORT
Definition WM_api.hh:1597
@ WM_JOB_PROGRESS
Definition WM_api.hh:1566
#define NC_SCENE
Definition WM_types.hh:345
#define ND_FRAME
Definition WM_types.hh:401
bool ABC_export(Scene *scene, bContext *C, const char *filepath, const AlembicExportParams *params, bool as_background_job)
static CLG_LogRef LOG
void set_export_subset(ExportSubset export_subset)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define LOG(severity)
Definition log.h:33
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
#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:16
Clock::time_point TimePoint
Definition BLI_timeit.hh:15
void print_duration(Nanoseconds duration)
Definition timeit.cc:42
char collection[MAX_IDPROP_NAME]
Definition ABC_alembic.h:63
Depsgraph * depsgraph
char filepath[FILE_MAX]
wmWindowManager * wm
AlembicExportParams params
blender::timeit::TimePoint start_time
void WM_report(eReportType type, const char *message)
void WM_reportf(eReportType type, const char *format,...)
void WM_set_locked_interface(wmWindowManager *wm, bool lock)
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:352
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:455
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:189
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:364
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:336