Blender  V3.3
abc_export_capi.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2020 Blender Foundation. All rights reserved. */
3 
4 #include "ABC_alembic.h"
5 #include "abc_archive.h"
7 #include "abc_subdiv_disabler.h"
8 
9 #include "MEM_guardedalloc.h"
10 
11 #include "DEG_depsgraph.h"
12 #include "DEG_depsgraph_build.h"
13 #include "DEG_depsgraph_query.h"
14 
15 #include "DNA_modifier_types.h"
16 #include "DNA_scene_types.h"
17 
18 #include "BKE_blender_version.h"
19 #include "BKE_context.h"
20 #include "BKE_global.h"
21 #include "BKE_main.h"
22 #include "BKE_scene.h"
23 
24 #include "BLI_fileops.h"
25 #include "BLI_path_util.h"
26 #include "BLI_string.h"
27 #include "BLI_timeit.hh"
28 
29 #include "WM_api.h"
30 #include "WM_types.h"
31 
32 #include "CLG_log.h"
33 static CLG_LogRef LOG = {"io.alembic"};
34 
35 #include <algorithm>
36 #include <memory>
37 
38 struct ExportJobData {
42 
45 
47  bool export_ok;
49 };
50 
51 namespace blender::io::alembic {
52 
53 /* Construct the depsgraph for exporting. */
54 static void build_depsgraph(Depsgraph *depsgraph, const bool visible_objects_only)
55 {
56  if (visible_objects_only) {
58  }
59  else {
61  }
62 }
63 
65 {
66  blender::timeit::Nanoseconds duration = blender::timeit::Clock::now() - data->start_time;
67  std::cout << "Alembic export of '" << data->filename << "' took ";
69  std::cout << '\n';
70 }
71 
72 static void export_startjob(void *customdata,
73  /* Cannot be const, this function implements wm_jobs_start_callback.
74  * NOLINTNEXTLINE: readability-non-const-parameter. */
75  short *stop,
76  short *do_update,
77  float *progress)
78 {
79  ExportJobData *data = static_cast<ExportJobData *>(customdata);
80  data->was_canceled = false;
81  data->start_time = blender::timeit::Clock::now();
82 
83  G.is_rendering = true;
84  WM_set_locked_interface(data->wm, true);
85  G.is_break = false;
86 
87  *progress = 0.0f;
88  *do_update = true;
89 
90  build_depsgraph(data->depsgraph, data->params.visible_objects_only);
91  SubdivModifierDisabler subdiv_disabler(data->depsgraph);
92  if (!data->params.apply_subdiv) {
93  subdiv_disabler.disable_modifiers();
94  }
95  BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
96 
97  /* For restoring the current frame after exporting animation is done. */
98  Scene *scene = DEG_get_input_scene(data->depsgraph);
99  const int orig_frame = scene->r.cfra;
100  const bool export_animation = (data->params.frame_start != data->params.frame_end);
101 
102  /* Create the Alembic archive. */
103  std::unique_ptr<ABCArchive> abc_archive;
104  try {
105  abc_archive = std::make_unique<ABCArchive>(
106  data->bmain, scene, data->params, std::string(data->filename));
107  }
108  catch (const std::exception &ex) {
109  std::stringstream error_message_stream;
110  error_message_stream << "Error writing to " << data->filename;
111  const std::string &error_message = error_message_stream.str();
112 
113  /* The exception message can be very cryptic (just "iostream error" on Linux, for example),
114  * so better not to include it in the report. */
115  CLOG_ERROR(&LOG, "%s: %s", error_message.c_str(), ex.what());
116  WM_report(RPT_ERROR, error_message.c_str());
117  data->export_ok = false;
118  return;
119  }
120  catch (...) {
121  /* Unknown exception class, so we cannot include its message. */
122  std::stringstream error_message_stream;
123  error_message_stream << "Unknown error writing to " << data->filename;
124  WM_report(RPT_ERROR, error_message_stream.str().c_str());
125  data->export_ok = false;
126  return;
127  }
128 
129  ABCHierarchyIterator iter(data->bmain, data->depsgraph, abc_archive.get(), data->params);
130 
131  if (export_animation) {
132  CLOG_INFO(&LOG, 2, "Exporting animation");
133 
134  /* Writing the animated frames is not 100% of the work, but it's our best guess. */
135  const float progress_per_frame = 1.0f / std::max(size_t(1), abc_archive->total_frame_count());
136  ABCArchive::Frames::const_iterator frame_it = abc_archive->frames_begin();
137  const ABCArchive::Frames::const_iterator frames_end = abc_archive->frames_end();
138 
139  for (; frame_it != frames_end; frame_it++) {
140  double frame = *frame_it;
141 
142  if (G.is_break || (stop != nullptr && *stop)) {
143  break;
144  }
145 
146  /* Update the scene for the next frame to render. */
147  scene->r.cfra = static_cast<int>(frame);
148  scene->r.subframe = static_cast<float>(frame - scene->r.cfra);
150 
151  CLOG_INFO(&LOG, 2, "Exporting frame %.2f", frame);
152  ExportSubset export_subset = abc_archive->export_subset_for_frame(frame);
153  iter.set_export_subset(export_subset);
154  iter.iterate_and_write();
155 
156  *progress += progress_per_frame;
157  *do_update = true;
158  }
159  }
160  else {
161  /* If we're not animating, a single iteration over all objects is enough. */
162  iter.iterate_and_write();
163  }
164 
165  iter.release_writers();
166 
167  /* Finish up by going back to the keyframe that was current before we started. */
168  if (scene->r.cfra != orig_frame) {
169  scene->r.cfra = orig_frame;
171  }
172 
173  data->export_ok = !data->was_canceled;
174 
175  *progress = 1.0f;
176  *do_update = true;
177 }
178 
179 static void export_endjob(void *customdata)
180 {
181  ExportJobData *data = static_cast<ExportJobData *>(customdata);
182 
183  DEG_graph_free(data->depsgraph);
184 
185  if (data->was_canceled && BLI_exists(data->filename)) {
186  BLI_delete(data->filename, false, false);
187  }
188 
189  G.is_rendering = false;
190  WM_set_locked_interface(data->wm, false);
192 }
193 
194 } // namespace blender::io::alembic
195 
197  bContext *C,
198  const char *filepath,
200  bool as_background_job)
201 {
202  ViewLayer *view_layer = CTX_data_view_layer(C);
203 
204  ExportJobData *job = static_cast<ExportJobData *>(
205  MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
206 
207  job->bmain = CTX_data_main(C);
208  job->wm = CTX_wm_manager(C);
209  job->export_ok = false;
210  BLI_strncpy(job->filename, filepath, sizeof(job->filename));
211 
212  job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode);
213  job->params = *params;
214 
215  bool export_ok = false;
216  if (as_background_job) {
217  wmJob *wm_job = WM_jobs_get(
218  job->wm, CTX_wm_window(C), scene, "Alembic Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC);
219 
220  /* setup job */
221  WM_jobs_customdata_set(wm_job, job, MEM_freeN);
222  WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
223  WM_jobs_callbacks(wm_job,
225  nullptr,
226  nullptr,
228 
229  WM_jobs_start(CTX_wm_manager(C), wm_job);
230  }
231  else {
232  /* Fake a job context, so that we don't need NULL pointer checks while exporting. */
233  short stop = 0, do_update = 0;
234  float progress = 0.0f;
235 
236  blender::io::alembic::export_startjob(job, &stop, &do_update, &progress);
238  export_ok = job->export_ok;
239 
240  MEM_freeN(job);
241  }
242 
243  return export_ok;
244 }
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bmain)
Definition: scene.cc:2648
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph)
Definition: scene.cc:2728
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:314
int BLI_delete(const char *file, bool dir, bool recursive) ATTR_NONNULL()
Definition: fileops.c:934
#define FILE_MAX
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
#define CLOG_ERROR(clg_ref,...)
Definition: CLG_log.h:190
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:187
Depsgraph * DEG_graph_new(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, eEvaluationMode mode)
Definition: depsgraph.cc:267
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
void DEG_graph_free(Depsgraph *graph)
Definition: depsgraph.cc:295
void DEG_graph_build_from_view_layer(struct Depsgraph *graph)
void DEG_graph_build_for_all_objects(struct Depsgraph *graph)
struct Scene * DEG_get_input_scene(const Depsgraph *graph)
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:25
@ WM_JOB_PROGRESS
Definition: WM_api.h:1339
@ WM_JOB_TYPE_ALEMBIC
Definition: WM_api.h:1366
#define NC_SCENE
Definition: WM_types.h:328
#define ND_FRAME
Definition: WM_types.h:382
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_)
Scene scene
const Depsgraph * depsgraph
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
#define G(x, y, z)
static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
static void export_endjob(void *customdata)
static void build_depsgraph(Depsgraph *depsgraph, const bool visible_objects_only)
static void report_job_duration(const ExportJobData *data)
Clock::time_point TimePoint
Definition: BLI_timeit.hh:14
void print_duration(Nanoseconds duration)
Definition: timeit.cc:10
std::chrono::nanoseconds Nanoseconds
Definition: BLI_timeit.hh:15
Depsgraph * depsgraph
wmWindowManager * wm
AlembicExportParams params
blender::timeit::TimePoint start_time
char filename[FILE_MAX]
Definition: BKE_main.h:121
struct RenderData r
Definition: wm_jobs.c:57
float max
void WM_report(eReportType type, const char *message)
void WM_set_locked_interface(wmWindowManager *wm, bool lock)
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition: wm_jobs.c:437
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition: wm_jobs.c:351
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *))
Definition: wm_jobs.c:323
void WM_jobs_timer(wmJob *wm_job, double timestep, unsigned int note, unsigned int endnote)
Definition: wm_jobs.c:339
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, int flag, int job_type)
Definition: wm_jobs.c:184