Alembic import/export: added as_background_job option
authorSybren A. Stüvel <sybren@stuvel.eu>
Wed, 19 Apr 2017 12:40:57 +0000 (14:40 +0200)
committerSybren A. Stüvel <sybren@stuvel.eu>
Wed, 19 Apr 2017 12:40:57 +0000 (14:40 +0200)
The ABC_export and ABC_import functions both take a as_background_job
parameter, and return a boolean.

When as_background_job=true, returns false immediately after scheduling
a background job. This was the old behaviour of this function, which makes
it very hard for scripts to do something with the data after the import
or export completes.

When as_background_job=false, performs the export synchronously, and
returns true when the export was ok, and false if there were any errors.
This allows further processing.

The Scene.alembic_export() function is deprecated, and will be removed from
Blender 2.8 in favour of calling the bpy.ops.wm.alembic_export() operator.
As such, it has been hard-coded to the old background job behaviour.

source/blender/alembic/ABC_alembic.h
source/blender/alembic/intern/alembic_capi.cc
source/blender/editors/io/io_alembic.c
source/blender/makesrna/intern/rna_scene_api.c
tests/python/alembic_tests.py

index ffe5700de12c481f7d4f7f375ba996ecdad07964..6b698e5ec66acde37fcdaa59c3414094a24c8507 100644 (file)
@@ -78,20 +78,32 @@ struct AlembicExportParams {
        float global_scale;
 };
 
-void ABC_export(
+/* The ABC_export and ABC_import functions both take a as_background_job
+ * parameter, and return a boolean.
+ *
+ * When as_background_job=true, returns false immediately after scheduling
+ * a background job.
+ *
+ * When as_background_job=false, performs the export synchronously, and returns
+ * true when the export was ok, and false if there were any errors.
+ */
+
+bool ABC_export(
         struct Scene *scene,
         struct bContext *C,
         const char *filepath,
-        const struct AlembicExportParams *params);
+        const struct AlembicExportParams *params,
+        bool as_background_job);
 
-void ABC_import(struct bContext *C,
+bool ABC_import(struct bContext *C,
                 const char *filepath,
                 float scale,
                 bool is_sequence,
                 bool set_frame_range,
                 int sequence_len,
                 int offset,
-                bool validate_meshes);
+                bool validate_meshes,
+                bool as_background_job);
 
 AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *object_paths);
 
index d260c6803dc0b107dd4dda5d227892653e168286..104b19beaf08fe8b50482fe85b571dadb1051eec 100644 (file)
@@ -238,6 +238,7 @@ struct ExportJobData {
        float *progress;
 
        bool was_canceled;
+       bool export_ok;
 };
 
 static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
@@ -271,6 +272,8 @@ static void export_startjob(void *customdata, short *stop, short *do_update, flo
                        BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain,
                                                      scene, scene->lay);
                }
+
+               data->export_ok = !data->was_canceled;
        }
        catch (const std::exception &e) {
                ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n';
@@ -297,15 +300,17 @@ static void export_endjob(void *customdata)
        BKE_spacedata_draw_locks(false);
 }
 
-void ABC_export(
+bool ABC_export(
         Scene *scene,
         bContext *C,
         const char *filepath,
-        const struct AlembicExportParams *params)
+        const struct AlembicExportParams *params,
+        bool as_background_job)
 {
        ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
        job->scene = scene;
        job->bmain = CTX_data_main(C);
+       job->export_ok = false;
        BLI_strncpy(job->filename, filepath, 1024);
 
        /* Alright, alright, alright....
@@ -354,19 +359,31 @@ void ABC_export(
                std::swap(job->settings.frame_start, job->settings.frame_end);
        }
 
-       wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
-                                   CTX_wm_window(C),
-                                   job->scene,
-                                   "Alembic Export",
-                                   WM_JOB_PROGRESS,
-                                   WM_JOB_TYPE_ALEMBIC);
+       if (as_background_job) {
+               wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+                                           CTX_wm_window(C),
+                                           job->scene,
+                                           "Alembic 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;
 
-       /* 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);
+               export_startjob(job, &stop, &do_update, &progress);
+               export_endjob(job);
+       }
 
-       WM_jobs_start(CTX_wm_manager(C), wm_job);
+       return job->export_ok;
 }
 
 /* ********************** Import file ********************** */
@@ -595,6 +612,7 @@ struct ImportJobData {
 
        char error_code;
        bool was_cancelled;
+       bool import_ok;
 };
 
 ABC_INLINE bool is_mesh_and_strands(const IObject &object)
@@ -803,6 +821,7 @@ static void import_endjob(void *user_data)
        switch (data->error_code) {
                default:
                case ABC_NO_ERROR:
+                       data->import_ok = !data->was_cancelled;
                        break;
                case ABC_ARCHIVE_FAIL:
                        WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail.");
@@ -818,13 +837,16 @@ static void import_freejob(void *user_data)
        delete data;
 }
 
-void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, bool validate_meshes)
+bool ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence,
+                bool set_frame_range, int sequence_len, int offset,
+                bool validate_meshes, bool as_background_job)
 {
        /* Using new here since MEM_* funcs do not call ctor to properly initialize
         * data. */
        ImportJobData *job = new ImportJobData();
        job->bmain = CTX_data_main(C);
        job->scene = CTX_data_scene(C);
+       job->import_ok = false;
        BLI_strncpy(job->filename, filepath, 1024);
 
        job->settings.scale = scale;
@@ -838,19 +860,31 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence
 
        G.is_break = false;
 
-       wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
-                                   CTX_wm_window(C),
-                                   job->scene,
-                                   "Alembic Import",
-                                   WM_JOB_PROGRESS,
-                                   WM_JOB_TYPE_ALEMBIC);
+       if (as_background_job) {
+               wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+                                                                       CTX_wm_window(C),
+                                                                       job->scene,
+                                                                       "Alembic Import",
+                                                                       WM_JOB_PROGRESS,
+                                                                       WM_JOB_TYPE_ALEMBIC);
 
-       /* setup job */
-       WM_jobs_customdata_set(wm_job, job, import_freejob);
-       WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
-       WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob);
+               /* setup job */
+               WM_jobs_customdata_set(wm_job, job, import_freejob);
+               WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
+               WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_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 importing. */
+               short stop = 0, do_update = 0;
+               float progress = 0.f;
+
+               import_startjob(job, &stop, &do_update, &progress);
+               import_endjob(job);
+       }
 
-       WM_jobs_start(CTX_wm_manager(C), wm_job);
+       return job->import_ok;
 }
 
 /* ************************************************************************** */
index 8651d89c403d2b0d0453afcecb52a38182daff52..62c36552048ee3bbddad0389901fdfb507c98689 100644 (file)
@@ -133,9 +133,10 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
            .global_scale = RNA_float_get(op->ptr, "global_scale"),
        };
 
-       ABC_export(CTX_data_scene(C), C, filename, &params);
+       const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job");
+       bool ok = ABC_export(CTX_data_scene(C), C, filename, &params, as_background_job);
 
-       return OPERATOR_FINISHED;
+       return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
 }
 
 static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
@@ -363,6 +364,9 @@ void WM_OT_alembic_export(wmOperatorType *ot)
        RNA_def_boolean(ot->srna, "export_hair", 1, "Export Hair", "Exports hair particle systems as animated curves");
        RNA_def_boolean(ot->srna, "export_particles", 1, "Export Particles", "Exports non-hair particle systems");
 
+       RNA_def_boolean(ot->srna, "as_background_job", true, "Run as Background Job",
+                       "Enable this to run the import in the background, disable to block Blender while importing");
+
        /* This dummy prop is used to check whether we need to init the start and
      * end frame values to that of the scene's, otherwise they are reset at
      * every change, draw update. */
@@ -497,6 +501,7 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op)
        const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence");
        const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range");
        const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
+       const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job");
 
        int offset = 0;
        int sequence_len = 1;
@@ -505,9 +510,11 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op)
                sequence_len = get_sequence_len(filename, &offset);
        }
 
-       ABC_import(C, filename, scale, is_sequence, set_frame_range, sequence_len, offset, validate_meshes);
+       bool ok = ABC_import(C, filename, scale, is_sequence, set_frame_range,
+                            sequence_len, offset, validate_meshes,
+                            as_background_job);
 
-       return OPERATOR_FINISHED;
+       return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
 }
 
 void WM_OT_alembic_import(wmOperatorType *ot)
@@ -538,6 +545,9 @@ void WM_OT_alembic_import(wmOperatorType *ot)
 
        RNA_def_boolean(ot->srna, "is_sequence", false, "Is Sequence",
                        "Set to true if the cache is split into separate files");
+
+       RNA_def_boolean(ot->srna, "as_background_job", true, "Run as Background Job",
+                       "Enable this to run the export in the background, disable to block Blender while exporting");
 }
 
 #endif
index 47b5c5a96abc9888111d54b2dde674779a256d0c..d3487fca42e0c9403617323f090e04260378436f 100644 (file)
@@ -254,7 +254,7 @@ static void rna_Scene_alembic_export(
            .global_scale = scale,
        };
 
-       ABC_export(scene, C, filepath, &params);
+       ABC_export(scene, C, filepath, &params, true);
 
 #ifdef WITH_PYTHON
        BPy_END_ALLOW_THREADS;
@@ -439,8 +439,9 @@ void RNA_api_scene(StructRNA *srna)
 #endif
 
 #ifdef WITH_ALEMBIC
+       /* XXX Deprecated, will be removed in 2.8 in favour of calling the export operator. */
        func = RNA_def_function(srna, "alembic_export", "rna_Scene_alembic_export");
-       RNA_def_function_ui_description(func, "Export to Alembic file");
+       RNA_def_function_ui_description(func, "Export to Alembic file (deprecated, use the Alembic export operator)");
 
        parm = RNA_def_string(func, "filepath", NULL, FILE_MAX, "File Path", "File path to write Alembic file");
        RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
index 4af69dd23fba1ddf216963088b11e2e48206b338..1cdfd75426ac8f35647e4f9b970db5f6222c0896 100755 (executable)
@@ -317,7 +317,8 @@ class HairParticlesExportTest(AbstractAlembicTest):
         abc = tempdir / 'hair-particles.abc'
         script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
                  "renderable_only=True, visible_layers_only=True, flatten=False, " \
-                 "export_hair=%r, export_particles=%r)" % (abc, export_hair, export_particles)
+                 "export_hair=%r, export_particles=%r, as_background_job=False)" \
+                 % (abc, export_hair, export_particles)
         self.run_blender('hair-particles.blend', script)
         return abc