Blender  V2.93
abc_export_capi.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2020 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 #include "ABC_alembic.h"
21 #include "abc_archive.h"
22 #include "abc_hierarchy_iterator.h"
23 #include "abc_subdiv_disabler.h"
24 
25 #include "MEM_guardedalloc.h"
26 
27 #include "DEG_depsgraph.h"
28 #include "DEG_depsgraph_build.h"
29 #include "DEG_depsgraph_query.h"
30 
31 #include "DNA_modifier_types.h"
32 #include "DNA_scene_types.h"
33 
34 #include "BKE_blender_version.h"
35 #include "BKE_context.h"
36 #include "BKE_global.h"
37 #include "BKE_main.h"
38 #include "BKE_scene.h"
39 
40 #include "BLI_fileops.h"
41 #include "BLI_path_util.h"
42 #include "BLI_string.h"
43 
44 #include "WM_api.h"
45 #include "WM_types.h"
46 
47 #include "CLG_log.h"
48 static CLG_LogRef LOG = {"io.alembic"};
49 
50 #include <algorithm>
51 #include <memory>
52 
53 struct ExportJobData {
57 
60 
62  bool export_ok;
63 };
64 
65 namespace blender::io::alembic {
66 
67 /* Construct the depsgraph for exporting. */
68 static void build_depsgraph(Depsgraph *depsgraph, const bool visible_objects_only)
69 {
70  if (visible_objects_only) {
72  }
73  else {
75  }
76 }
77 
78 static void export_startjob(void *customdata,
79  /* Cannot be const, this function implements wm_jobs_start_callback.
80  * NOLINTNEXTLINE: readability-non-const-parameter. */
81  short *stop,
82  short *do_update,
83  float *progress)
84 {
85  ExportJobData *data = static_cast<ExportJobData *>(customdata);
86  data->was_canceled = false;
87 
88  G.is_rendering = true;
89  WM_set_locked_interface(data->wm, true);
90  G.is_break = false;
91 
92  *progress = 0.0f;
93  *do_update = true;
94 
95  build_depsgraph(data->depsgraph, data->params.visible_objects_only);
96  SubdivModifierDisabler subdiv_disabler(data->depsgraph);
97  if (!data->params.apply_subdiv) {
98  subdiv_disabler.disable_modifiers();
99  }
100  BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
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 = 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->filename));
112  }
113  catch (const std::exception &ex) {
114  std::stringstream error_message_stream;
115  error_message_stream << "Error writing to " << data->filename;
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_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->filename;
129  WM_report(RPT_ERROR, error_message_stream.str().c_str());
130  data->export_ok = false;
131  return;
132  }
133 
134  ABCHierarchyIterator iter(data->depsgraph, abc_archive.get(), data->params);
135 
136  if (export_animation) {
137  CLOG_INFO(&LOG, 2, "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 || (stop != nullptr && *stop)) {
148  break;
149  }
150 
151  /* Update the scene for the next frame to render. */
152  scene->r.cfra = static_cast<int>(frame);
153  scene->r.subframe = frame - scene->r.cfra;
155 
156  CLOG_INFO(&LOG, 2, "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  *progress += progress_per_frame;
162  *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 (CFRA != orig_frame) {
174  CFRA = orig_frame;
176  }
177 
178  data->export_ok = !data->was_canceled;
179 
180  *progress = 1.0f;
181  *do_update = true;
182 }
183 
184 static 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->filename)) {
191  BLI_delete(data->filename, false, false);
192  }
193 
194  G.is_rendering = false;
195  WM_set_locked_interface(data->wm, false);
196 }
197 
198 } // namespace blender::io::alembic
199 
201  bContext *C,
202  const char *filepath,
204  bool as_background_job)
205 {
206  ViewLayer *view_layer = CTX_data_view_layer(C);
207 
208  ExportJobData *job = static_cast<ExportJobData *>(
209  MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
210 
211  job->bmain = CTX_data_main(C);
212  job->wm = CTX_wm_manager(C);
213  job->export_ok = false;
214  BLI_strncpy(job->filename, filepath, sizeof(job->filename));
215 
216  job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode);
217  job->params = *params;
218 
219  bool export_ok = false;
220  if (as_background_job) {
221  wmJob *wm_job = WM_jobs_get(
222  job->wm, CTX_wm_window(C), scene, "Alembic Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC);
223 
224  /* setup job */
225  WM_jobs_customdata_set(wm_job, job, MEM_freeN);
226  WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
227  WM_jobs_callbacks(wm_job,
229  nullptr,
230  nullptr,
232 
233  WM_jobs_start(CTX_wm_manager(C), wm_job);
234  }
235  else {
236  /* Fake a job context, so that we don't need NULL pointer checks while exporting. */
237  short stop = 0, do_update = 0;
238  float progress = 0.0f;
239 
240  blender::io::alembic::export_startjob(job, &stop, &do_update, &progress);
242  export_ok = job->export_ok;
243 
244  MEM_freeN(job);
245  }
246 
247  return export_ok;
248 }
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:689
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1044
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1018
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:699
void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bmain)
Definition: scene.c:2713
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph)
Definition: scene.c:2794
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:349
int BLI_delete(const char *file, bool dir, bool recursive) ATTR_NONNULL()
Definition: fileops.c:1037
#define FILE_MAX
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
#define CLOG_ERROR(clg_ref,...)
Definition: CLG_log.h:204
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:201
Depsgraph * DEG_graph_new(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, eEvaluationMode mode)
Definition: depsgraph.cc:281
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:51
void DEG_graph_free(Depsgraph *graph)
Definition: depsgraph.cc:314
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)
#define CFRA
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:39
@ WM_JOB_TYPE_ALEMBIC
Definition: WM_api.h:751
@ WM_JOB_PROGRESS
Definition: WM_api.h:726
#define NC_SCENE
Definition: WM_types.h:279
#define ND_FRAME
Definition: WM_types.h:334
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:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
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)
Depsgraph * depsgraph
wmWindowManager * wm
AlembicExportParams params
char filename[FILE_MAX]
Definition: BKE_main.h:116
struct RenderData r
Definition: wm_jobs.c:73
float max
#define G(x, y, z)
void WM_report(ReportType 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:450
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, void *owner, const char *name, int flag, int job_type)
Definition: wm_jobs.c:196
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:372
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *))
Definition: wm_jobs.c:344
void WM_jobs_timer(wmJob *wm_job, double timestep, unsigned int note, unsigned int endnote)
Definition: wm_jobs.c:360