Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/io/io_cache_library.c')
-rw-r--r--source/blender/editors/io/io_cache_library.c961
1 files changed, 961 insertions, 0 deletions
diff --git a/source/blender/editors/io/io_cache_library.c b/source/blender/editors/io/io_cache_library.c
new file mode 100644
index 00000000000..f7076ae0cae
--- /dev/null
+++ b/source/blender/editors/io/io_cache_library.c
@@ -0,0 +1,961 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/io/io_cache_library.c
+ * \ingroup editor/io
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLF_translation.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_fileops.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_path_util.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_cache_library_types.h"
+#include "DNA_group_types.h"
+#include "DNA_listBase.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_force.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+
+#include "BKE_anim.h"
+#include "BKE_blender.h"
+#include "BKE_depsgraph.h"
+#include "BKE_cache_library.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+
+#include "ED_screen.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "io_cache_library.h"
+
+#include "PTC_api.h"
+
+static int ED_cache_library_active_object_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if (!(ob && (ob->transflag & OB_DUPLIGROUP) && ob->dup_group && ob->cache_library))
+ return false;
+
+ return true;
+}
+
+static int ED_cache_modifier_poll(bContext *C)
+{
+ if (!ED_cache_library_active_object_poll(C))
+ return false;
+ if (!CTX_data_pointer_get_type(C, "cache_modifier", &RNA_CacheLibraryModifier).data)
+ return false;
+
+ return true;
+}
+
+/********************** new cache library operator *********************/
+
+static int new_cachelib_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+ Main *bmain = CTX_data_main(C);
+ PointerRNA ptr, idptr;
+ PropertyRNA *prop;
+
+ /* add or copy material */
+ if (cachelib) {
+ cachelib = BKE_cache_library_copy(cachelib);
+ }
+ else {
+ cachelib = BKE_cache_library_add(bmain, DATA_("CacheLibrary"));
+ }
+
+ /* enable fake user by default */
+ cachelib->id.flag |= LIB_FAKEUSER;
+
+ /* hook into UI */
+ UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
+
+ if (prop) {
+ /* when creating new ID blocks, use is already 1, but RNA
+ * pointer se also increases user, so this compensates it */
+ cachelib->id.us--;
+
+ RNA_id_pointer_create(&cachelib->id, &idptr);
+ RNA_property_pointer_set(&ptr, prop, idptr);
+ RNA_property_update(C, &ptr, prop);
+ }
+
+ WM_event_add_notifier(C, NC_SCENE, cachelib);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_new(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "New Cache Library";
+ ot->idname = "CACHELIBRARY_OT_new";
+ ot->description = "Add a new cache library";
+
+ /* api callbacks */
+ ot->poll = ED_operator_object_active;
+ ot->exec = new_cachelib_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+}
+
+/********************** delete cache library operator *********************/
+
+static int cache_library_delete_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+
+ BKE_cache_library_unlink(cachelib);
+ BKE_libblock_free(bmain, cachelib);
+
+ WM_event_add_notifier(C, NC_SCENE, cachelib);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_delete(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Delete Cache Library";
+ ot->idname = "CACHELIBRARY_OT_delete";
+ ot->description = "Delete a cache library data block";
+
+ /* api callbacks */
+ ot->exec = cache_library_delete_exec;
+ ot->invoke = WM_operator_confirm;
+ ot->poll = ED_cache_library_active_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO;
+}
+
+/********************** bake cache operator *********************/
+
+typedef enum eCacheLibraryBake_EvalMode {
+ CACHELIBRARY_BAKE_PREVIEW = (1 << 0), /* evaluate data with preview settings */
+ CACHELIBRARY_BAKE_RENDER = (1 << 1), /* evaluate data with render settings */
+} eCacheLibraryBake_EvalMode;
+
+static int cache_library_bake_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if (!ob || !(ob->transflag & OB_DUPLIGROUP) || !ob->dup_group || !ob->cache_library)
+ return false;
+ /* disable when the result is not displayed, just to avoid confusing situations */
+ if (ob->cache_library->display_mode != CACHE_LIBRARY_DISPLAY_RESULT)
+ return false;
+
+ return true;
+}
+
+typedef struct CacheLibraryBakeJob {
+ short *stop, *do_update;
+ float *progress;
+
+ struct Main *bmain;
+ struct Scene *scene;
+ struct CacheLibrary *cachelib;
+ int lay;
+ float mat[4][4];
+ struct Group *group;
+
+ eCacheLibraryBake_EvalMode eval_mode;
+ EvaluationContext eval_ctx;
+
+ struct PTCWriterArchive *archive;
+ struct PTCWriter *writer;
+
+ int start_frame, end_frame;
+ int origfra; /* original frame to reset scene after export */
+ float origframelen; /* original frame length to reset scene after export */
+} CacheLibraryBakeJob;
+
+static bool cache_library_bake_stop(CacheLibraryBakeJob *data)
+{
+ return (*data->stop) || G.is_break;
+}
+
+static void cache_library_bake_set_progress(CacheLibraryBakeJob *data, float progress)
+{
+ *data->do_update = 1;
+ *data->progress = progress;
+}
+
+static void cache_library_bake_set_particle_baking(Main *bmain, bool baking)
+{
+ /* XXX would be nicer to just loop over scene->base here,
+ * but this does not catch all objects included in dupli groups ...
+ */
+ Object *ob;
+ for (ob = bmain->object.first; ob; ob = ob->id.next) {
+ ParticleSystem *psys;
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if (baking)
+ psys->pointcache->flag |= PTCACHE_BAKING;
+ else
+ psys->pointcache->flag &= ~PTCACHE_BAKING;
+ }
+ }
+}
+
+static void cache_library_bake_do(CacheLibraryBakeJob *data, bool use_render)
+{
+ Scene *scene = data->scene;
+ int frame, frame_prev, start_frame, end_frame;
+ CacheProcessData process_data;
+
+ if (cache_library_bake_stop(data))
+ return;
+
+ /* === prepare === */
+
+ process_data.lay = data->lay;
+ copy_m4_m4(process_data.mat, data->mat);
+ process_data.dupcache = BKE_dupli_cache_new();
+
+ switch (data->cachelib->source_mode) {
+ case CACHE_LIBRARY_SOURCE_SCENE:
+ data->writer = PTC_writer_dupligroup(data->group->id.name, &data->eval_ctx, scene, data->group, data->cachelib);
+ break;
+ case CACHE_LIBRARY_SOURCE_CACHE:
+ data->writer = PTC_writer_duplicache(data->group->id.name, data->group, process_data.dupcache, data->cachelib->data_types, G.debug & G_DEBUG_SIMDATA);
+ break;
+ }
+ if (!data->writer) {
+ BKE_dupli_cache_free(process_data.dupcache);
+ return;
+ }
+
+ data->cachelib->flag |= CACHE_LIBRARY_BAKING;
+
+ PTC_writer_init(data->writer, data->archive);
+
+ start_frame = data->start_frame;
+ end_frame = data->end_frame;
+
+ /* === frame loop === */
+
+ cache_library_bake_set_progress(data, 0.0f);
+ for (frame = frame_prev = start_frame; frame <= end_frame; frame_prev = frame++) {
+
+ const bool init_strands = (frame == start_frame);
+
+ printf("Bake Cache '%s' | Frame %d\n", data->group->id.name+2, frame);
+
+ /* XXX Ugly, but necessary to avoid particle caching of paths when not needed.
+ * This takes a lot of time, but is only needed in the first frame.
+ */
+ cache_library_bake_set_particle_baking(data->bmain, !init_strands);
+
+ scene->r.cfra = frame;
+ BKE_scene_update_group_for_newframe(&data->eval_ctx, data->bmain, scene, data->group, scene->lay);
+
+ switch (data->cachelib->source_mode) {
+ case CACHE_LIBRARY_SOURCE_SCENE:
+ BKE_dupli_cache_from_group(scene, data->group, data->cachelib, process_data.dupcache, &data->eval_ctx, init_strands);
+ break;
+ case CACHE_LIBRARY_SOURCE_CACHE:
+ BKE_cache_read_dupli_cache(data->cachelib, process_data.dupcache, scene, data->group, frame, use_render, false);
+ break;
+ }
+
+ BKE_cache_process_dupli_cache(data->cachelib, &process_data, scene, data->group, frame_prev, frame, true, false, true);
+
+ PTC_write_sample(data->writer);
+
+ cache_library_bake_set_progress(data, (float)(frame - start_frame + 1) / (float)(end_frame - start_frame + 1));
+ if (cache_library_bake_stop(data))
+ break;
+ }
+
+ /* === cleanup === */
+
+ if (data->writer) {
+ PTC_writer_free(data->writer);
+ data->writer = NULL;
+ }
+
+ data->cachelib->flag &= ~CACHE_LIBRARY_BAKING;
+ cache_library_bake_set_particle_baking(data->bmain, false);
+
+ BKE_dupli_cache_free(process_data.dupcache);
+}
+
+/* Warning! Deletes existing files if possible, operator should show confirm dialog! */
+static bool cache_library_bake_ensure_file_target(const char *filename)
+{
+ if (BLI_exists(filename)) {
+ if (BLI_is_dir(filename)) {
+ return false;
+ }
+ else if (BLI_is_file(filename)) {
+ if (BLI_file_is_writable(filename)) {
+ /* returns 0 on success */
+ return (BLI_delete(filename, false, false) == 0);
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void cache_library_bake_start(void *customdata, short *stop, short *do_update, float *progress)
+{
+ CacheLibraryBakeJob *data = (CacheLibraryBakeJob *)customdata;
+ const bool do_preview = data->eval_mode & CACHELIBRARY_BAKE_PREVIEW;
+ const bool do_render = data->eval_mode & CACHELIBRARY_BAKE_RENDER;
+ const PTCArchiveResolution archive_res = (do_preview ? PTC_RESOLUTION_PREVIEW : 0) | (do_render ? PTC_RESOLUTION_RENDER : 0);
+ Scene *scene = data->scene;
+ char filename[FILE_MAX];
+ char app_name[MAX_NAME];
+ IDProperty *metadata;
+
+ data->stop = stop;
+ data->do_update = do_update;
+ data->progress = progress;
+
+ data->origfra = scene->r.cfra;
+ data->origframelen = scene->r.framelen;
+ scene->r.framelen = 1.0f;
+
+ BKE_cache_archive_output_path(data->cachelib, filename, sizeof(filename));
+ BLI_snprintf(app_name, sizeof(app_name), "Blender %s", versionstr);
+
+ metadata = BKE_cache_library_get_output_metadata(data->cachelib, false);
+
+ data->archive = PTC_open_writer_archive(FPS, data->start_frame, filename, archive_res, app_name, data->cachelib->description, NULL, metadata);
+
+ if (data->archive) {
+
+ G.is_break = false;
+
+ if (do_preview) {
+ data->eval_ctx.mode = DAG_EVAL_VIEWPORT;
+ PTC_writer_archive_use_render(data->archive, false);
+ cache_library_bake_do(data, false);
+ }
+
+ if (do_render) {
+ data->eval_ctx.mode = DAG_EVAL_RENDER;
+ PTC_writer_archive_use_render(data->archive, true);
+ cache_library_bake_do(data, true);
+ }
+
+ }
+
+ *do_update = true;
+ *stop = 0;
+}
+
+static void cache_library_bake_end(void *customdata)
+{
+ CacheLibraryBakeJob *data = (CacheLibraryBakeJob *)customdata;
+ Scene *scene = data->scene;
+
+ G.is_rendering = false;
+ BKE_spacedata_draw_locks(false);
+
+ if (data->writer)
+ PTC_writer_free(data->writer);
+ if (data->archive)
+ PTC_close_writer_archive(data->archive);
+
+ /* reset scene frame */
+ scene->r.cfra = data->origfra;
+ scene->r.framelen = data->origframelen;
+ BKE_scene_update_for_newframe(&data->eval_ctx, data->bmain, scene, scene->lay);
+}
+
+static void cache_library_bake_init(CacheLibraryBakeJob *data, bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+
+ /* make sure we can write */
+ char filename[FILE_MAX];
+ BKE_cache_archive_output_path(cachelib, filename, sizeof(filename));
+ cache_library_bake_ensure_file_target(filename);
+
+ /* XXX annoying hack: needed to prevent data corruption when changing
+ * scene frame in separate threads
+ */
+ G.is_rendering = true;
+
+ BKE_spacedata_draw_locks(true);
+
+ /* setup data */
+ data->bmain = bmain;
+ data->scene = scene;
+ data->cachelib = cachelib;
+ data->lay = ob->lay;
+ copy_m4_m4(data->mat, ob->obmat);
+ data->group = ob->dup_group;
+
+ data->eval_mode = RNA_enum_get(op->ptr, "eval_mode");
+
+ if (RNA_struct_property_is_set(op->ptr, "start_frame"))
+ data->start_frame = RNA_int_get(op->ptr, "start_frame");
+ else
+ data->start_frame = scene->r.sfra;
+ if (RNA_struct_property_is_set(op->ptr, "end_frame"))
+ data->end_frame = RNA_int_get(op->ptr, "end_frame");
+ else
+ data->end_frame = scene->r.efra;
+}
+
+static void cache_library_bake_freejob(void *customdata)
+{
+ CacheLibraryBakeJob *data= (CacheLibraryBakeJob *)customdata;
+ MEM_freeN(data);
+}
+
+static int cache_library_bake_exec(bContext *C, wmOperator *op)
+{
+ const bool use_job = RNA_boolean_get(op->ptr, "use_job");
+
+ if (use_job) {
+ /* when running through invoke, run as a job */
+ CacheLibraryBakeJob *data;
+ wmJob *wm_job;
+
+ /* XXX set WM_JOB_EXCL_RENDER to prevent conflicts with render jobs,
+ * since we need to set G.is_rendering
+ */
+ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_data_scene(C), "Cache Library Bake",
+ WM_JOB_PROGRESS | WM_JOB_EXCL_RENDER, WM_JOB_TYPE_CACHELIBRARY_BAKE);
+
+ /* setup data */
+ data = MEM_callocN(sizeof(CacheLibraryBakeJob), "Cache Library Bake Job");
+ cache_library_bake_init(data, C, op);
+
+ WM_jobs_customdata_set(wm_job, data, cache_library_bake_freejob);
+ WM_jobs_timer(wm_job, 0.1, NC_SCENE|ND_FRAME, NC_SCENE|ND_FRAME);
+ WM_jobs_callbacks(wm_job, cache_library_bake_start, NULL, NULL, cache_library_bake_end);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+ WM_cursor_wait(0);
+
+ /* add modal handler for ESC */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ else {
+ /* in direct execution mode we run this operator blocking instead of using a job */
+ CacheLibraryBakeJob data;
+ short stop = false, do_update = false;
+ float progress = 0.0f;
+
+ cache_library_bake_init(&data, C, op);
+
+ cache_library_bake_start(&data, &stop, &do_update, &progress);
+ cache_library_bake_end(&data);
+
+ return OPERATOR_FINISHED;
+ }
+}
+
+static int cache_library_bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+
+ char filename[FILE_MAX];
+
+ if (!cachelib)
+ return OPERATOR_CANCELLED;
+
+ /* make sure we run a job when exec is called after confirm popup */
+ RNA_boolean_set(op->ptr, "use_job", true);
+
+ BKE_cache_archive_output_path(cachelib, filename, sizeof(filename));
+
+ if (!BKE_cache_archive_path_test(cachelib, cachelib->output_filepath)) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot create file path for cache library %200s", cachelib->id.name+2);
+ return OPERATOR_CANCELLED;
+ }
+
+ if (BLI_exists(filename)) {
+ if (BLI_is_dir(filename)) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cache Library target is a directory: %200s", filename);
+ return OPERATOR_CANCELLED;
+ }
+ else if (BLI_is_file(filename)) {
+ if (BLI_file_is_writable(filename)) {
+ return WM_operator_confirm_message(C, op, "Overwrite?");
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot overwrite Cache Library target: %200s", filename);
+ return OPERATOR_CANCELLED;
+ }
+
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Invalid Cache Library target: %200s", filename);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else {
+ return cache_library_bake_exec(C, op);
+ }
+}
+
+/* catch esc */
+static int cache_library_bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ /* no running job, remove handler and pass through */
+ if (!WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_CACHELIBRARY_BAKE))
+ return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
+
+ /* running bake */
+ switch (event->type) {
+ case ESCKEY:
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_PASS_THROUGH;
+}
+
+void CACHELIBRARY_OT_bake(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ static EnumPropertyItem eval_mode_items[] = {
+ {CACHELIBRARY_BAKE_PREVIEW, "PREVIEW", ICON_RESTRICT_VIEW_OFF, "Preview", "Evaluate data with preview settings"},
+ {CACHELIBRARY_BAKE_RENDER, "RENDER", ICON_RESTRICT_RENDER_OFF, "Render", "Evaluate data with render settings"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Bake";
+ ot->description = "Bake cache library";
+ ot->idname = "CACHELIBRARY_OT_bake";
+
+ /* api callbacks */
+ ot->invoke = cache_library_bake_invoke;
+ ot->exec = cache_library_bake_exec;
+ ot->modal = cache_library_bake_modal;
+ ot->poll = cache_library_bake_poll;
+
+ /* flags */
+ /* no undo for this operator, cannot restore old cache files anyway */
+ ot->flag = OPTYPE_REGISTER;
+
+ prop = RNA_def_boolean(ot->srna, "use_job", false, "Use Job", "Run operator as a job");
+ /* This is in internal property set by the invoke function.
+ * It allows the exec function to be called from both the confirm popup
+ * as well as a direct exec call for running a blocking operator in background mode.
+ */
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ prop = RNA_def_enum(ot->srna, "eval_mode", eval_mode_items, CACHELIBRARY_BAKE_RENDER, "Evaluation Mode", "Mode to use when evaluating data");
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+
+ RNA_def_int(ot->srna, "start_frame", 0, INT_MIN, INT_MAX, "Start Frame", "First frame to be cached", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "end_frame", 0, INT_MIN, INT_MAX, "End Frame", "Last frame to be cached", INT_MIN, INT_MAX);
+}
+
+/* ========================================================================= */
+
+static int cache_library_archive_slice_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+ Scene *scene = CTX_data_scene(C);
+
+ const int start_frame = RNA_int_get(op->ptr, "start_frame");
+ const int end_frame = RNA_int_get(op->ptr, "end_frame");
+
+ char input_filepath[FILE_MAX], input_filename[FILE_MAX];
+ char output_filepath[FILE_MAX], output_filename[FILE_MAX];
+ struct PTCReaderArchive *input_archive;
+ struct PTCWriterArchive *output_archive;
+ PTCArchiveResolution archive_res;
+ CacheArchiveInfo info;
+ IDProperty *metadata;
+
+ RNA_string_get(op->ptr, "input_filepath", input_filepath);
+ if (input_filepath[0] == '\0')
+ return OPERATOR_CANCELLED;
+ RNA_string_get(op->ptr, "output_filepath", output_filepath);
+ if (output_filepath[0] == '\0')
+ return OPERATOR_CANCELLED;
+
+ BKE_cache_archive_path_ex(input_filepath, cachelib->id.lib, NULL, input_filename, sizeof(input_filename));
+ BKE_cache_archive_path_ex(output_filepath, cachelib->id.lib, NULL, output_filename, sizeof(output_filename));
+
+ /* make sure we can write */
+ cache_library_bake_ensure_file_target(output_filename);
+
+ input_archive = PTC_open_reader_archive(scene, input_filename);
+ if (!input_archive) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot open cache file at '%s'", input_filepath);
+ return OPERATOR_CANCELLED;
+ }
+
+ archive_res = PTC_reader_archive_get_resolutions(input_archive);
+ {
+ IDPropertyTemplate val;
+ val.i = 0;
+ metadata = IDP_New(IDP_GROUP, &val, "cache input metadata");
+ }
+ PTC_get_archive_info(input_archive, &info, metadata);
+
+ output_archive = PTC_open_writer_archive(FPS, start_frame, output_filename, archive_res, info.app_name, info.description, NULL, metadata);
+
+ IDP_FreeProperty(metadata);
+ MEM_freeN(metadata);
+
+ if (!output_archive) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot write to cache file at '%s'", output_filepath);
+ return OPERATOR_CANCELLED;
+ }
+
+ PTC_archive_slice(input_archive, output_archive, start_frame, end_frame);
+
+ PTC_close_reader_archive(input_archive);
+ PTC_close_writer_archive(output_archive);
+
+ return OPERATOR_FINISHED;
+}
+
+static int cache_library_archive_slice_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ return WM_operator_props_popup_confirm(C, op, event);
+
+#if 0
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+
+ char output_filename[FILE_MAX];
+
+ if (!cachelib)
+ return OPERATOR_CANCELLED;
+
+ /* make sure we run a job when exec is called after confirm popup */
+ RNA_boolean_set(op->ptr, "use_job", true);
+
+ RNA_string_get(op->ptr, "output_filepath", output_filepath);
+ if (output_filepath[0] == '\0')
+ return OPERATOR_CANCELLED;
+ BKE_cache_archive_output_path(cachelib, output_filename, sizeof(output_filename));
+
+ if (!BKE_cache_archive_path_test(cachelib, output_filepath)) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot create file path for cache library %200s", cachelib->id.name+2);
+ return OPERATOR_CANCELLED;
+ }
+
+ if (BLI_exists(output_filename)) {
+ if (BLI_is_dir(output_filename)) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cache Library target is a directory: %200s", output_filename);
+ return OPERATOR_CANCELLED;
+ }
+ else if (BLI_is_file(output_filename)) {
+ if (BLI_file_is_writable(output_filename)) {
+ return WM_operator_confirm_message(C, op, "Overwrite?");
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot overwrite Cache Library target: %200s", output_filename);
+ return OPERATOR_CANCELLED;
+ }
+
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Invalid Cache Library target: %200s", output_filename);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else {
+ return cache_library_bake_exec(C, op);
+ }
+#endif
+}
+
+void CACHELIBRARY_OT_archive_slice(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Archive Slice";
+ ot->description = "Copy a range of frames to a new cache archive";
+ ot->idname = "CACHELIBRARY_OT_archive_slice";
+
+ /* api callbacks */
+ ot->exec = cache_library_archive_slice_exec;
+ ot->invoke = cache_library_archive_slice_invoke;
+ ot->poll = ED_cache_library_active_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_boolean(ot->srna, "use_job", false, "Use Job", "Run operator as a job");
+ /* This is in internal property set by the invoke function.
+ * It allows the exec function to be called from both the confirm popup
+ * as well as a direct exec call for running a blocking operator in background mode.
+ */
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ prop = RNA_def_string(ot->srna, "input_filepath", NULL, FILE_MAX, "Input File Path", "Path to the source cache archive");
+ RNA_def_property_subtype(prop, PROP_FILEPATH);
+ prop = RNA_def_string(ot->srna, "output_filepath", NULL, FILE_MAX, "Output File Path", "Path to the target cache archive");
+ RNA_def_property_subtype(prop, PROP_FILEPATH);
+ RNA_def_int(ot->srna, "start_frame", 1, INT_MIN, INT_MAX, "Start Frame", "First frame to copy", 1, 10000);
+ RNA_def_int(ot->srna, "end_frame", 250, INT_MIN, INT_MAX, "End Frame", "Last frame to copy", 1, 10000);
+}
+
+/* ========================================================================= */
+
+#if 0
+static void ui_item_nlabel(uiLayout *layout, const char *s, size_t len)
+{
+ char buf[256];
+
+ BLI_strncpy(buf, s, sizeof(buf)-1);
+ buf[min_ii(len, sizeof(buf)-1)] = '\0';
+
+ uiItemL(layout, buf, ICON_NONE);
+}
+
+static void archive_info_labels(uiLayout *layout, const char *info)
+{
+ const char delim[] = {'\n', '\0'};
+ const char *cur = info;
+ size_t linelen;
+ char *sep, *suf;
+
+ linelen = BLI_str_partition(cur, delim, &sep, &suf);
+ while (sep) {
+ ui_item_nlabel(layout, cur, linelen);
+ cur = suf;
+
+ linelen = BLI_str_partition(cur, delim, &sep, &suf);
+ }
+ ui_item_nlabel(layout, cur, linelen);
+}
+
+static uiBlock *archive_info_popup_create(bContext *C, ARegion *ar, void *arg)
+{
+ const char *info = arg;
+ uiBlock *block;
+ uiLayout *layout;
+
+ block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
+ UI_block_flag_disable(block, UI_BLOCK_LOOP);
+ UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
+
+ layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, UI_UNIT_X * 20, UI_UNIT_Y, 0, UI_style_get());
+
+ archive_info_labels(layout, info);
+
+ UI_block_bounds_set_centered(block, 0);
+ UI_block_direction_set(block, UI_DIR_DOWN);
+
+ return block;
+}
+#endif
+
+static void print_stream(void *UNUSED(userdata), const char *s)
+{
+ printf("%s", s);
+}
+
+static int cache_library_archive_info_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+ Scene *scene = CTX_data_scene(C);
+
+ const bool use_cache_info = RNA_boolean_get(op->ptr, "use_cache_info");
+ const bool calc_bytes_size = RNA_boolean_get(op->ptr, "calc_bytes_size");
+ const bool use_stdout = RNA_boolean_get(op->ptr, "use_stdout");
+ const bool use_popup = RNA_boolean_get(op->ptr, "use_popup");
+ const bool use_clipboard = RNA_boolean_get(op->ptr, "use_clipboard");
+
+ char filepath[FILE_MAX], filename[FILE_MAX];
+ struct PTCReaderArchive *archive;
+
+ RNA_string_get(op->ptr, "filepath", filepath);
+ if (filepath[0] == '\0')
+ return OPERATOR_CANCELLED;
+
+ BKE_cache_archive_path_ex(filepath, cachelib->id.lib, NULL, filename, sizeof(filename));
+ archive = PTC_open_reader_archive(scene, filename);
+ if (!archive) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot open cache file at '%s'", filepath);
+ return OPERATOR_CANCELLED;
+ }
+
+ if (use_cache_info) {
+ if (cachelib->archive_info)
+ BKE_cache_archive_info_clear(cachelib->archive_info);
+ else
+ cachelib->archive_info = BKE_cache_archive_info_new();
+
+ BLI_strncpy(cachelib->archive_info->filepath, filename, sizeof(cachelib->archive_info->filepath));
+
+ PTC_get_archive_info_nodes(archive, cachelib->archive_info, calc_bytes_size);
+ }
+
+ if (use_stdout) {
+ PTC_get_archive_info_stream(archive, print_stream, NULL);
+ }
+
+ if (use_popup) {
+// UI_popup_block_invoke(C, archive_info_popup_create, info);
+ }
+
+ if (use_clipboard) {
+// WM_clipboard_text_set(info, false);
+ }
+
+ PTC_close_reader_archive(archive);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_archive_info(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Archive Info";
+ ot->description = "Get archive details from a cache library archive";
+ ot->idname = "CACHELIBRARY_OT_archive_info";
+
+ /* api callbacks */
+ ot->exec = cache_library_archive_info_exec;
+ ot->poll = ED_cache_library_active_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_string(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Path to the cache archive");
+ RNA_def_boolean(ot->srna, "use_cache_info", false, "Use Cache Library Info", "Store info in the cache library");
+ RNA_def_boolean(ot->srna, "calc_bytes_size", false, "Calculate Size", "Calculate overall size of nodes in bytes (can take a while)");
+ RNA_def_boolean(ot->srna, "use_stdout", false, "Use stdout", "Print info in standard output");
+ RNA_def_boolean(ot->srna, "use_popup", false, "Show Popup", "Display archive info in a popup");
+ RNA_def_boolean(ot->srna, "use_clipboard", false, "Copy to Clipboard", "Copy archive info to the clipboard");
+}
+
+/* ------------------------------------------------------------------------- */
+/* Cache Modifiers */
+
+static int cache_library_add_modifier_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+
+ eCacheModifier_Type type = RNA_enum_get(op->ptr, "type");
+ if (type == eCacheModifierType_None) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_cache_modifier_add(cachelib, NULL, type);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_add_modifier(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Cache Modifier";
+ ot->description = "Add a cache modifier";
+ ot->idname = "CACHELIBRARY_OT_add_modifier";
+
+ /* api callbacks */
+ ot->exec = cache_library_add_modifier_exec;
+ ot->poll = ED_cache_library_active_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "type", cache_modifier_type_items, eCacheModifierType_None, "Type", "Type of modifier to add");
+}
+
+static int cache_library_remove_modifier_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA md_ptr = CTX_data_pointer_get_type(C, "cache_modifier", &RNA_CacheLibraryModifier);
+ CacheModifier *md = md_ptr.data;
+ CacheLibrary *cachelib = md_ptr.id.data;
+
+ if (!md)
+ return OPERATOR_CANCELLED;
+
+ BKE_cache_modifier_remove(cachelib, md);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_remove_modifier(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Cache Modifier";
+ ot->description = "Remove a cache modifier";
+ ot->idname = "CACHELIBRARY_OT_remove_modifier";
+
+ /* api callbacks */
+ ot->exec = cache_library_remove_modifier_exec;
+ ot->poll = ED_cache_modifier_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}