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/pointcache')
-rw-r--r--source/blender/pointcache/CMakeLists.txt66
-rw-r--r--source/blender/pointcache/PTC_api.cpp423
-rw-r--r--source/blender/pointcache/PTC_api.h138
-rw-r--r--source/blender/pointcache/SConscript58
-rw-r--r--source/blender/pointcache/alembic/CMakeLists.txt75
-rw-r--r--source/blender/pointcache/alembic/SConscript62
-rw-r--r--source/blender/pointcache/alembic/abc_cloth.cpp237
-rw-r--r--source/blender/pointcache/alembic/abc_cloth.h68
-rw-r--r--source/blender/pointcache/alembic/abc_customdata.cpp804
-rw-r--r--source/blender/pointcache/alembic/abc_customdata.h175
-rw-r--r--source/blender/pointcache/alembic/abc_frame_mapper.cpp52
-rw-r--r--source/blender/pointcache/alembic/abc_frame_mapper.h57
-rw-r--r--source/blender/pointcache/alembic/abc_group.cpp770
-rw-r--r--source/blender/pointcache/alembic/abc_group.h226
-rw-r--r--source/blender/pointcache/alembic/abc_info.cpp516
-rw-r--r--source/blender/pointcache/alembic/abc_interpolate.cpp32
-rw-r--r--source/blender/pointcache/alembic/abc_interpolate.h236
-rw-r--r--source/blender/pointcache/alembic/abc_mesh.cpp630
-rw-r--r--source/blender/pointcache/alembic/abc_mesh.h153
-rw-r--r--source/blender/pointcache/alembic/abc_object.cpp161
-rw-r--r--source/blender/pointcache/alembic/abc_object.h84
-rw-r--r--source/blender/pointcache/alembic/abc_particles.cpp1284
-rw-r--r--source/blender/pointcache/alembic/abc_particles.h222
-rw-r--r--source/blender/pointcache/alembic/abc_reader.cpp194
-rw-r--r--source/blender/pointcache/alembic/abc_reader.h108
-rw-r--r--source/blender/pointcache/alembic/abc_schema.h107
-rw-r--r--source/blender/pointcache/alembic/abc_simdebug.cpp185
-rw-r--r--source/blender/pointcache/alembic/abc_simdebug.h82
-rw-r--r--source/blender/pointcache/alembic/abc_split.cpp236
-rw-r--r--source/blender/pointcache/alembic/abc_writer.cpp132
-rw-r--r--source/blender/pointcache/alembic/abc_writer.h130
-rw-r--r--source/blender/pointcache/alembic/alembic.cpp168
-rw-r--r--source/blender/pointcache/alembic/alembic.h48
-rw-r--r--source/blender/pointcache/intern/ptc_types.cpp44
-rw-r--r--source/blender/pointcache/intern/ptc_types.h237
-rw-r--r--source/blender/pointcache/intern/reader.cpp57
-rw-r--r--source/blender/pointcache/intern/reader.h69
-rw-r--r--source/blender/pointcache/intern/writer.cpp56
-rw-r--r--source/blender/pointcache/intern/writer.h59
-rw-r--r--source/blender/pointcache/util/util_error_handler.cpp102
-rw-r--r--source/blender/pointcache/util/util_error_handler.h201
-rw-r--r--source/blender/pointcache/util/util_function.h48
-rw-r--r--source/blender/pointcache/util/util_task.cpp87
-rw-r--r--source/blender/pointcache/util/util_task.h51
-rw-r--r--source/blender/pointcache/util/util_thread.h74
-rw-r--r--source/blender/pointcache/util/util_types.h53
46 files changed, 9057 insertions, 0 deletions
diff --git a/source/blender/pointcache/CMakeLists.txt b/source/blender/pointcache/CMakeLists.txt
new file mode 100644
index 00000000000..2898785c11e
--- /dev/null
+++ b/source/blender/pointcache/CMakeLists.txt
@@ -0,0 +1,66 @@
+# ***** 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) 2013, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ intern
+ util
+ ../blenkernel
+ ../blenlib
+ ../makesdna
+ ../makesrna
+ ../../../intern/guardedalloc
+)
+
+set(INC_SYS
+)
+
+set(SRC
+ intern/ptc_types.h
+ intern/ptc_types.cpp
+ intern/reader.h
+ intern/reader.cpp
+ intern/writer.h
+ intern/writer.cpp
+
+ util/util_error_handler.h
+ util/util_error_handler.cpp
+ util/util_task.cpp
+ util/util_task.h
+ util/util_thread.h
+ util/util_types.h
+
+ PTC_api.h
+ PTC_api.cpp
+)
+
+if(NOT WITH_CPP11)
+ list(APPEND INC_SYS ${BOOST_INCLUDE_DIR})
+endif()
+
+if(WITH_ALEMBIC)
+ add_definitions(-DWITH_PTC_ALEMBIC)
+ add_subdirectory(alembic)
+endif()
+
+blender_add_lib(bf_pointcache "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/pointcache/PTC_api.cpp b/source/blender/pointcache/PTC_api.cpp
new file mode 100644
index 00000000000..767d4451120
--- /dev/null
+++ b/source/blender/pointcache/PTC_api.cpp
@@ -0,0 +1,423 @@
+/*
+ * ***** 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.
+ *
+ * Copyright 2013, Blender Foundation.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "PTC_api.h"
+
+#include "util/util_error_handler.h"
+
+#include "reader.h"
+#include "writer.h"
+
+#include "ptc_types.h"
+
+extern "C" {
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "DNA_listBase.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_modifier.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+
+#include "RNA_access.h"
+}
+
+using namespace PTC;
+
+class StubFactory : public Factory {
+ const std::string &get_default_extension() { static std::string ext = ""; return ext; }
+ WriterArchive *open_writer_archive(double /*fps*/, float /*start_frame*/, const std::string &/*name*/, PTCArchiveResolution /*resolutions*/,
+ const char */*app_name*/, const char */*description*/, const struct tm */*time*/, struct IDProperty */*metadata*/, ErrorHandler */*error_handler*/) { return NULL; }
+ ReaderArchive *open_reader_archive(double /*fps*/, float /*start_frame*/, const std::string &/*name*/, ErrorHandler * /*error_handler*/) { return NULL; }
+ void slice(ReaderArchive * /*in*/, WriterArchive * /*out*/, struct ListBase * /*slices*/) {}
+ Writer *create_writer_object(const std::string &/*name*/, Scene */*scene*/, Object */*ob*/) { return NULL; }
+ Reader *create_reader_object(const std::string &/*name*/, Object */*ob*/) { return NULL; }
+ Writer *create_writer_group(const std::string &/*name*/, Group */*group*/) { return NULL; }
+ Reader *create_reader_group(const std::string &/*name*/, Group */*group*/) { return NULL; }
+ Writer *create_writer_cloth(const std::string &/*name*/, Object */*ob*/, ClothModifierData */*clmd*/) { return NULL; }
+ Reader *create_reader_cloth(const std::string &/*name*/, Object */*ob*/, ClothModifierData */*clmd*/) { return NULL; }
+ Writer *create_writer_derived_mesh(const std::string &/*name*/, Object */*ob*/, DerivedMesh **/*dm_ptr*/) { return NULL; }
+ Reader *create_reader_derived_mesh(const std::string &/*name*/, Object */*ob*/) { return NULL; }
+ Writer *create_writer_derived_final_realtime(const std::string &/*name*/, Object */*ob*/) { return NULL; }
+ Writer *create_writer_derived_final_render(const std::string &/*name*/, Scene */*scene*/, Object */*ob*/, DerivedMesh **/*render_dm_ptr*/) { return NULL; }
+ Writer *create_writer_dupligroup(const std::string &/*name*/, EvaluationContext */*eval_ctx*/, Scene */*scene*/, Group */*group*/, CacheLibrary */*cachelib*/) { return NULL; }
+ Writer *create_writer_duplicache(const std::string &/*name*/, Group */*group*/, DupliCache */*dupcache*/, int /*datatypes*/, bool /*do_sim_debug*/) { return NULL; }
+ Reader *create_reader_duplicache(const std::string &/*name*/, Group */*group*/, DupliCache */*dupcache*/, bool /*read_strands_motion*/, bool /*read_strands_children*/, bool /*do_sim_debug*/) { return NULL; }
+ Reader *create_reader_duplicache_object(const std::string &/*name*/, Object */*ob*/, DupliObjectData */*data*/, bool /*read_strands_motion*/, bool /*read_strands_children*/) { return NULL; }
+};
+
+#ifndef WITH_PTC_ALEMBIC
+void PTC_alembic_init()
+{
+ static StubFactory stub_factory;
+ PTC::Factory::alembic = &stub_factory;
+}
+#endif
+
+void PTC_error_handler_std(void)
+{
+ ErrorHandler::clear_default_handler();
+}
+
+void PTC_error_handler_callback(PTCErrorCallback cb, void *userdata)
+{
+ ErrorHandler::set_default_handler(new CallbackErrorHandler(cb, userdata));
+}
+
+static ReportType report_type_from_error_level(PTCErrorLevel level)
+{
+ switch (level) {
+ case PTC_ERROR_NONE: return RPT_DEBUG;
+ case PTC_ERROR_INFO: return RPT_INFO;
+ case PTC_ERROR_WARNING: return RPT_WARNING;
+ case PTC_ERROR_CRITICAL: return RPT_ERROR;
+ }
+ return RPT_ERROR;
+}
+
+static void error_handler_reports_cb(void *vreports, PTCErrorLevel level, const char *message)
+{
+ ReportList *reports = (ReportList *)vreports;
+
+ BKE_report(reports, report_type_from_error_level(level), message);
+}
+
+void PTC_error_handler_reports(struct ReportList *reports)
+{
+ ErrorHandler::set_default_handler(new CallbackErrorHandler(error_handler_reports_cb, reports));
+}
+
+static void error_handler_modifier_cb(void *vmd, PTCErrorLevel UNUSED(level), const char *message)
+{
+ ModifierData *md = (ModifierData *)vmd;
+
+ modifier_setError(md, "%s", message);
+}
+
+void PTC_error_handler_modifier(struct ModifierData *md)
+{
+ ErrorHandler::set_default_handler(new CallbackErrorHandler(error_handler_modifier_cb, md));
+}
+
+
+const char *PTC_get_default_archive_extension(void)
+{
+ return PTC::Factory::alembic->get_default_extension().c_str();
+}
+
+PTCWriterArchive *PTC_open_writer_archive(double fps, float start_frame, const char *path, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata)
+{
+ return (PTCWriterArchive *)PTC::Factory::alembic->open_writer_archive(fps, start_frame, path, resolutions, app_name, description, time, metadata, NULL);
+}
+
+void PTC_close_writer_archive(PTCWriterArchive *_archive)
+{
+ PTC::WriterArchive *archive = (PTC::WriterArchive *)_archive;
+ delete archive;
+}
+
+void PTC_writer_archive_use_render(PTCWriterArchive *_archive, bool enable)
+{
+ PTC::WriterArchive *archive = (PTC::WriterArchive *)_archive;
+ archive->use_render(enable);
+}
+
+PTCReaderArchive *PTC_open_reader_archive(Scene *scene, const char *path)
+{
+ double fps = FPS;
+ float start_frame = scene->r.sfra;
+ return PTC_open_reader_archive_ex(fps, start_frame, path);
+}
+
+PTCReaderArchive *PTC_open_reader_archive_ex(double fps, float start_frame, const char *path)
+{
+ return (PTCReaderArchive *)PTC::Factory::alembic->open_reader_archive(fps, start_frame, path, NULL);
+}
+
+void PTC_close_reader_archive(PTCReaderArchive *_archive)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ delete archive;
+}
+
+PTCArchiveResolution PTC_reader_archive_get_resolutions(PTCReaderArchive *_archive)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ return archive->get_resolutions();
+}
+
+void PTC_reader_archive_use_render(PTCReaderArchive *_archive, bool enable)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ archive->use_render(enable);
+}
+
+bool PTC_reader_archive_get_frame_range(PTCReaderArchive *_archive, int *start_frame, int *end_frame)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ return archive->get_frame_range(*start_frame, *end_frame);
+}
+
+void PTC_writer_init(PTCWriter *_writer, PTCWriterArchive *_archive)
+{
+ PTC::Writer *writer = (PTC::Writer *)_writer;
+ PTC::WriterArchive *archive = (PTC::WriterArchive *)_archive;
+ writer->init(archive);
+}
+
+void PTC_writer_create_refs(PTCWriter *_writer)
+{
+ PTC::Writer *writer = (PTC::Writer *)_writer;
+ writer->create_refs();
+}
+
+void PTC_reader_init(PTCReader *_reader, PTCReaderArchive *_archive)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ reader->init(archive);
+}
+
+/* ========================================================================= */
+
+void PTC_writer_free(PTCWriter *_writer)
+{
+ PTC::Writer *writer = (PTC::Writer *)_writer;
+ delete writer;
+}
+
+void PTC_write_sample(struct PTCWriter *_writer)
+{
+ PTC::Writer *writer = (PTC::Writer *)_writer;
+ writer->write_sample();
+}
+
+
+void PTC_reader_free(PTCReader *_reader)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ delete reader;
+}
+
+bool PTC_reader_get_frame_range(PTCReader *_reader, int *start_frame, int *end_frame)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ int sfra, efra;
+ if (reader->get_frame_range(sfra, efra)) {
+ if (start_frame) *start_frame = sfra;
+ if (end_frame) *end_frame = efra;
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+PTCReadSampleResult PTC_read_sample(PTCReader *_reader, float frame)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ return reader->read_sample(frame);
+}
+
+PTCReadSampleResult PTC_test_sample(PTCReader *_reader, float frame)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ return reader->test_sample(frame);
+}
+
+void PTC_get_archive_info_stream(PTCReaderArchive *_archive, void (*stream)(void *, const char *), void *userdata)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ archive->get_info_stream(stream, userdata);
+}
+
+void PTC_get_archive_info(PTCReaderArchive *_archive, struct CacheArchiveInfo *info, IDProperty *metadata)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ archive->get_info(info, metadata);
+}
+
+void PTC_get_archive_info_nodes(PTCReaderArchive *_archive, struct CacheArchiveInfo *info, bool calc_bytes_size)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ archive->get_info_nodes(info, calc_bytes_size);
+}
+
+void PTC_archive_slice(PTCReaderArchive *_in, PTCWriterArchive *_out, struct ListBase *slices)
+{
+ PTC::ReaderArchive *in = (PTC::ReaderArchive *)_in;
+ PTC::WriterArchive *out = (PTC::WriterArchive *)_out;
+
+ PTC::Factory::alembic->slice(in, out, slices);
+}
+
+
+PTCWriter *PTC_writer_dupligroup(const char *name, struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group, struct CacheLibrary *cachelib)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_dupligroup(name, eval_ctx, scene, group, cachelib);
+}
+
+PTCWriter *PTC_writer_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache, int datatypes, bool do_sim_debug)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_duplicache(name, group, dupcache, datatypes, do_sim_debug);
+}
+
+PTCReader *PTC_reader_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_duplicache(name, group, dupcache,
+ read_strands_motion, read_strands_children, read_sim_debug);
+}
+
+PTCReader *PTC_reader_duplicache_object(const char *name, struct Object *ob, struct DupliObjectData *data,
+ bool read_strands_motion, bool read_strands_children)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_duplicache_object(name, ob, data, read_strands_motion, read_strands_children);
+}
+
+
+/* get writer/reader from RNA type */
+PTCWriter *PTC_writer_from_rna(Scene */*scene*/, PointerRNA */*ptr*/)
+{
+#if 0
+#if 0
+ if (RNA_struct_is_a(ptr->type, &RNA_ParticleSystem)) {
+ Object *ob = (Object *)ptr->id.data;
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ return PTC_writer_particles_combined(scene, ob, psys);
+ }
+#endif
+ if (RNA_struct_is_a(ptr->type, &RNA_ClothModifier)) {
+ Object *ob = (Object *)ptr->id.data;
+ ClothModifierData *clmd = (ClothModifierData *)ptr->data;
+ return PTC_writer_cloth(scene, ob, clmd);
+ }
+#endif
+ return NULL;
+}
+
+PTCReader *PTC_reader_from_rna(Scene */*scene*/, PointerRNA */*ptr*/)
+{
+#if 0
+ if (RNA_struct_is_a(ptr->type, &RNA_ParticleSystem)) {
+ Object *ob = (Object *)ptr->id.data;
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ /* XXX particles are bad ...
+ * this can be either the actual particle cache or the hair dynamics cache,
+ * which is actually the cache of the internal cloth modifier
+ */
+ bool use_cloth_cache = psys->part->type == PART_HAIR && (psys->flag & PSYS_HAIR_DYNAMICS);
+ if (use_cloth_cache && psys->clmd)
+ return PTC_reader_cloth(scene, ob, psys->clmd);
+ else
+ return PTC_reader_particles(scene, ob, psys);
+ }
+ if (RNA_struct_is_a(ptr->type, &RNA_ClothModifier)) {
+ Object *ob = (Object *)ptr->id.data;
+ ClothModifierData *clmd = (ClothModifierData *)ptr->data;
+ return PTC_reader_cloth(scene, ob, clmd);
+ }
+#endif
+ return NULL;
+}
+
+
+/* ==== CLOTH ==== */
+
+PTCWriter *PTC_writer_cloth(const char *name, Object *ob, ClothModifierData *clmd)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_cloth(name, ob, clmd);
+}
+
+PTCReader *PTC_reader_cloth(const char *name, Object *ob, ClothModifierData *clmd)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_cloth(name, ob, clmd);
+}
+
+
+/* ==== MESH ==== */
+
+PTCWriter *PTC_writer_derived_mesh(const char *name, Object *ob, DerivedMesh **dm_ptr)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_derived_mesh(name, ob, dm_ptr);
+}
+
+PTCReader *PTC_reader_derived_mesh(const char *name, Object *ob)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_derived_mesh(name, ob);
+}
+
+struct DerivedMesh *PTC_reader_derived_mesh_acquire_result(PTCReader *_reader)
+{
+ DerivedMeshReader *reader = (DerivedMeshReader *)_reader;
+ return reader->acquire_result();
+}
+
+void PTC_reader_derived_mesh_discard_result(PTCReader *_reader)
+{
+ DerivedMeshReader *reader = (DerivedMeshReader *)_reader;
+ reader->discard_result();
+}
+
+
+PTCWriter *PTC_writer_derived_final_realtime(const char *name, Object *ob)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_derived_final_realtime(name, ob);
+}
+
+PTCWriter *PTC_writer_derived_final_render(const char *name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_derived_final_render(name, scene, ob, render_dm_ptr);
+}
+
+
+/* ==== OBJECT ==== */
+
+PTCWriter *PTC_writer_object(const char *name, Scene *scene, Object *ob)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_object(name, scene, ob);
+}
+
+PTCReader *PTC_reader_object(const char *name, Object *ob)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_object(name, ob);
+}
+
+
+/* ==== GROUP ==== */
+
+PTCWriter *PTC_writer_group(const char *name, Group *group)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_group(name, group);
+}
+
+PTCReader *PTC_reader_group(const char *name, Group *group)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_writer_group(name, group);
+}
diff --git a/source/blender/pointcache/PTC_api.h b/source/blender/pointcache/PTC_api.h
new file mode 100644
index 00000000000..e164cd5d177
--- /dev/null
+++ b/source/blender/pointcache/PTC_api.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __PTC_API_H__
+#define __PTC_API_H__
+
+#include "util/util_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tm;
+
+struct Main;
+struct Scene;
+struct EvaluationContext;
+struct ListBase;
+struct PointerRNA;
+struct ReportList;
+struct CacheArchiveInfo;
+
+struct DupliCache;
+struct ClothModifierData;
+struct DerivedMesh;
+struct Group;
+struct IDProperty;
+struct ModifierData;
+struct Object;
+struct ParticleSystem;
+struct SoftBody;
+
+struct PTCWriterArchive;
+struct PTCReaderArchive;
+struct PTCWriter;
+struct PTCReader;
+
+void PTC_alembic_init(void);
+
+/*** Error Handling ***/
+void PTC_error_handler_std(void);
+void PTC_error_handler_callback(PTCErrorCallback cb, void *userdata);
+void PTC_error_handler_reports(struct ReportList *reports);
+void PTC_error_handler_modifier(struct ModifierData *md);
+
+/*** Archive ***/
+
+const char *PTC_get_default_archive_extension(void);
+
+struct PTCWriterArchive *PTC_open_writer_archive(double fps, float start_frame, const char *path, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata);
+void PTC_close_writer_archive(struct PTCWriterArchive *archive);
+void PTC_writer_archive_use_render(struct PTCWriterArchive *archive, bool enable);
+
+struct PTCReaderArchive *PTC_open_reader_archive(struct Scene *scene, const char *path);
+struct PTCReaderArchive *PTC_open_reader_archive_ex(double fps, float start_frame, const char *path);
+void PTC_close_reader_archive(struct PTCReaderArchive *archive);
+PTCArchiveResolution PTC_reader_archive_get_resolutions(struct PTCReaderArchive *archive);
+void PTC_reader_archive_use_render(struct PTCReaderArchive *archive, bool enable);
+bool PTC_reader_archive_get_frame_range(struct PTCReaderArchive *_archive, int *start_frame, int *end_frame);
+
+void PTC_writer_init(struct PTCWriter *writer, struct PTCWriterArchive *archive);
+void PTC_writer_create_refs(struct PTCWriter *writer);
+void PTC_reader_init(struct PTCReader *reader, struct PTCReaderArchive *archive);
+
+/*** Reader/Writer Interface ***/
+
+void PTC_writer_free(struct PTCWriter *writer);
+void PTC_write_sample(struct PTCWriter *writer);
+
+void PTC_reader_free(struct PTCReader *reader);
+bool PTC_reader_get_frame_range(struct PTCReader *reader, int *start_frame, int *end_frame);
+PTCReadSampleResult PTC_read_sample(struct PTCReader *reader, float frame);
+PTCReadSampleResult PTC_test_sample(struct PTCReader *reader, float frame);
+
+void PTC_get_archive_info_stream(struct PTCReaderArchive *archive, void (*stream)(void *, const char *), void *userdata);
+void PTC_get_archive_info(struct PTCReaderArchive *_archive, struct CacheArchiveInfo *info, struct IDProperty *metadata);
+void PTC_get_archive_info_nodes(struct PTCReaderArchive *_archive, struct CacheArchiveInfo *info, bool calc_bytes_size);
+
+typedef struct CacheSlice {
+ struct CacheSlice *next, *prev;
+ int start, end;
+} CacheSlice;
+
+void PTC_archive_slice(struct PTCReaderArchive *in, struct PTCWriterArchive *out, struct ListBase *slices);
+
+struct PTCWriter *PTC_writer_dupligroup(const char *name, struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group, struct CacheLibrary *cachelib);
+struct PTCWriter *PTC_writer_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache, int datatypes, bool do_sim_debug);
+
+struct PTCReader *PTC_reader_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug);
+struct PTCReader *PTC_reader_duplicache_object(const char *name, struct Object *ob, struct DupliObjectData *data,
+ bool read_strands_motion, bool read_strands_children);
+
+/* get writer/reader from RNA type */
+struct PTCWriter *PTC_writer_from_rna(struct Scene *scene, struct PointerRNA *ptr);
+struct PTCReader *PTC_reader_from_rna(struct Scene *scene, struct PointerRNA *ptr);
+
+/* Object */
+struct PTCWriter *PTC_writer_object(const char *name, struct Scene *scene, struct Object *ob);
+struct PTCReader *PTC_reader_object(const char *name, struct Object *ob);
+
+/* Group */
+struct PTCWriter *PTC_writer_group(const char *name, struct Group *group);
+struct PTCReader *PTC_reader_group(const char *name, struct Group *group);
+
+/* Cloth */
+struct PTCWriter *PTC_writer_cloth(const char *name, struct Object *ob, struct ClothModifierData *clmd);
+struct PTCReader *PTC_reader_cloth(const char *name, struct Object *ob, struct ClothModifierData *clmd);
+
+struct PTCWriter *PTC_writer_derived_mesh(const char *name, struct Object *ob, struct DerivedMesh **dm_ptr);
+struct PTCReader *PTC_reader_derived_mesh(const char *name, struct Object *ob);
+struct DerivedMesh *PTC_reader_derived_mesh_acquire_result(struct PTCReader *reader);
+void PTC_reader_derived_mesh_discard_result(struct PTCReader *reader);
+
+struct PTCWriter *PTC_writer_derived_final_realtime(const char *name, struct Object *ob);
+struct PTCWriter *PTC_writer_derived_final_render(const char *name, struct Scene *scene, struct Object *ob, struct DerivedMesh **render_dm_ptr);
+
+#ifdef __cplusplus
+} /* extern C */
+#endif
+
+#endif /* __PTC_API_H__ */
diff --git a/source/blender/pointcache/SConscript b/source/blender/pointcache/SConscript
new file mode 100644
index 00000000000..f94bbd43668
--- /dev/null
+++ b/source/blender/pointcache/SConscript
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+#
+# ***** 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) 2014, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Lukas Toenne.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+Import ('env')
+
+sources = env.Glob('intern/*.cpp') + env.Glob('util/*.cpp') + env.Glob('*.cpp')
+
+incs = [
+ '.',
+ 'intern',
+ 'util',
+ '../blenkernel',
+ '../blenlib',
+ '../makesdna',
+ '../makesrna',
+ '../../../intern/guardedalloc',
+ ]
+
+defs = []
+
+if not env['WITH_BF_CPP11']:
+ incs.append(env['BF_BOOST_INC'])
+
+if env['WITH_BF_INTERNATIONAL']:
+ defs.append('WITH_INTERNATIONAL')
+
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'):
+ incs.append(env['BF_PTHREADS_INC'])
+
+if env['WITH_BF_ALEMBIC']:
+ defs.append('WITH_PTC_ALEMBIC')
+ SConscript(['alembic/SConscript'])
+
+env.BlenderLib('bf_pointcache', sources, incs, defines=defs, libtype=['core','player'], priority=[902,902])
diff --git a/source/blender/pointcache/alembic/CMakeLists.txt b/source/blender/pointcache/alembic/CMakeLists.txt
new file mode 100644
index 00000000000..d7391428d5d
--- /dev/null
+++ b/source/blender/pointcache/alembic/CMakeLists.txt
@@ -0,0 +1,75 @@
+# ***** 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) 2013, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ../
+ ../intern
+ ../util
+ ../../blenkernel
+ ../../blenlib
+ ../../makesdna
+ ../../makesrna
+ ../../../../intern/guardedalloc
+)
+
+set(INC_SYS
+ ${ALEMBIC_INCLUDE_DIRS}
+ ${OPENEXR_INCLUDE_DIR}/OpenEXR
+)
+
+set(SRC
+ alembic.cpp
+ alembic.h
+
+ abc_frame_mapper.cpp
+ abc_frame_mapper.h
+ abc_info.cpp
+ abc_interpolate.cpp
+ abc_interpolate.h
+ abc_reader.cpp
+ abc_reader.h
+ abc_schema.h
+ abc_split.cpp
+ abc_writer.cpp
+ abc_writer.h
+
+ abc_cloth.cpp
+ abc_cloth.h
+ abc_customdata.cpp
+ abc_customdata.h
+ abc_group.cpp
+ abc_group.h
+ abc_mesh.cpp
+ abc_mesh.h
+ abc_object.cpp
+ abc_object.h
+ abc_particles.cpp
+ abc_particles.h
+ abc_simdebug.cpp
+ abc_simdebug.h
+)
+
+add_definitions(-DWITH_ALEMBIC)
+
+blender_add_lib(bf_pointcache_alembic "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/pointcache/alembic/SConscript b/source/blender/pointcache/alembic/SConscript
new file mode 100644
index 00000000000..b9da2df57c2
--- /dev/null
+++ b/source/blender/pointcache/alembic/SConscript
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+#
+# ***** 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) 2014, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Lukas Toenne.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+Import ('env')
+
+sources = env.Glob('*.cpp')
+
+incs = [
+ '.',
+ '../',
+ '../intern',
+ '../util',
+ '../../blenkernel',
+ '../../blenlib',
+ '../../makesdna',
+ '../../makesrna',
+ '../../../../intern/guardedalloc',
+ ]
+
+incs += Split(env['BF_OPENEXR_INC'])
+
+defs = []
+
+if env['WITH_BF_INTERNATIONAL']:
+ defs.append('WITH_INTERNATIONAL')
+
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'):
+ incs.append(env['BF_PTHREADS_INC'])
+
+incs.append(env['BF_OPENEXR_INC'])
+incs.append(env['BF_ALEMBIC_INC'])
+
+if not env['WITH_BF_CPP11']:
+ incs.append(env['BF_BOOST_INC'])
+
+defs.append('WITH_ALEMBIC')
+
+env.BlenderLib('bf_pointcache_alembic', sources, incs, defines=defs, libtype=['core','player'], priority=[901, 901])
diff --git a/source/blender/pointcache/alembic/abc_cloth.cpp b/source/blender/pointcache/alembic/abc_cloth.cpp
new file mode 100644
index 00000000000..a722bc2b725
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_cloth.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "abc_cloth.h"
+
+extern "C" {
+#include "BLI_math.h"
+
+#include "DNA_cloth_types.h"
+#include "DNA_object_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BKE_cloth.h"
+}
+
+#include "PTC_api.h"
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+AbcClothWriter::AbcClothWriter(const std::string &name, Object *ob, ClothModifierData *clmd) :
+ ClothWriter(ob, clmd, name)
+{
+ set_error_handler(new ModifierErrorHandler(&clmd->modifier));
+}
+
+AbcClothWriter::~AbcClothWriter()
+{
+}
+
+void AbcClothWriter::init_abc(OObject parent)
+{
+ if (m_points)
+ return;
+
+ m_points = OPoints(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OPointsSchema &schema = m_points.getSchema();
+ OCompoundProperty geom_params = schema.getArbGeomParams();
+
+ m_param_velocities = OV3fGeomParam(geom_params, "velocities", false, kVaryingScope, 1, 0);
+ m_param_goal_positions = OP3fGeomParam(geom_params, "goal_positions", false, kVaryingScope, 1, 0);
+}
+
+static V3fArraySample create_sample_velocities(Cloth *cloth, std::vector<V3f> &data)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ data.reserve(totvert);
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ float *co = vert->v;
+ data.push_back(V3f(co[0], co[1], co[2]));
+ }
+
+ return V3fArraySample(data);
+}
+
+static P3fArraySample create_sample_goal_positions(Cloth *cloth, std::vector<V3f> &data)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ data.reserve(totvert);
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ float *co = vert->xconst;
+ data.push_back(V3f(co[0], co[1], co[2]));
+ }
+
+ return P3fArraySample(data);
+}
+
+void AbcClothWriter::write_sample()
+{
+ if (!m_points)
+ return;
+
+ Cloth *cloth = m_clmd->clothObject;
+ if (!cloth)
+ return;
+
+ OPointsSchema &schema = m_points.getSchema();
+
+ int totpoint = cloth->numverts;
+ ClothVertex *vert;
+ int i;
+
+ /* XXX TODO only needed for the first frame/sample */
+ std::vector<Util::uint64_t> ids;
+ ids.reserve(totpoint);
+ for (i = 0, vert = cloth->verts; i < totpoint; ++i, ++vert)
+ ids.push_back(i);
+
+ std::vector<V3f> positions;
+ positions.reserve(totpoint);
+ for (i = 0, vert = cloth->verts; i < totpoint; ++i, ++vert) {
+ float *co = vert->x;
+ positions.push_back(V3f(co[0], co[1], co[2]));
+ }
+
+ std::vector<V3f> velocities_buffer;
+ std::vector<V3f> goal_positions_buffer;
+ V3fArraySample velocities = create_sample_velocities(cloth, velocities_buffer);
+ P3fArraySample goal_positions = create_sample_goal_positions(cloth, goal_positions_buffer);
+
+ OPointsSchema::Sample sample = OPointsSchema::Sample(V3fArraySample(positions), UInt64ArraySample(ids));
+ schema.set(sample);
+
+ m_param_velocities.set(OV3fGeomParam::Sample(velocities, kVaryingScope));
+ m_param_goal_positions.set(OP3fGeomParam::Sample(goal_positions, kVaryingScope));
+}
+
+
+AbcClothReader::AbcClothReader(const std::string &name, Object *ob, ClothModifierData *clmd) :
+ ClothReader(ob, clmd, name)
+{
+ set_error_handler(new ModifierErrorHandler(&clmd->modifier));
+}
+
+AbcClothReader::~AbcClothReader()
+{
+}
+
+void AbcClothReader::init_abc(IObject object)
+{
+ if (m_points)
+ return;
+ m_points = IPoints(object, kWrapExisting);
+
+ IPointsSchema &schema = m_points.getSchema();
+ ICompoundProperty geom_params = schema.getArbGeomParams();
+
+ m_param_velocities = IV3fGeomParam(geom_params, "velocities", 0);
+ m_param_goal_positions = IP3fGeomParam(geom_params, "goal_positions", 0);
+}
+
+static void apply_sample_positions(Cloth *cloth, P3fArraySamplePtr sample)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ BLI_assert(sample->size() == totvert);
+
+ const V3f *data = sample->get();
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ const V3f &co = data[i];
+ copy_v3_v3(vert->x, co.getValue());
+ }
+}
+
+static void apply_sample_velocities(Cloth *cloth, V3fArraySamplePtr sample)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ BLI_assert(sample->size() == totvert);
+
+ const V3f *data = sample->get();
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ const V3f &vel = data[i];
+ copy_v3_v3(vert->v, vel.getValue());
+ }
+}
+
+static void apply_sample_goal_positions(Cloth *cloth, P3fArraySamplePtr sample)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ BLI_assert(sample->size() == totvert);
+
+ const V3f *data = sample->get();
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ const V3f &co = data[i];
+ copy_v3_v3(vert->xconst, co.getValue());
+ }
+}
+
+PTCReadSampleResult AbcClothReader::read_sample_abc(chrono_t time)
+{
+ Cloth *cloth = m_clmd->clothObject;
+
+ if (!m_points)
+ return PTC_READ_SAMPLE_INVALID;
+
+ IPointsSchema &schema = m_points.getSchema();
+ if (schema.getNumSamples() == 0)
+ return PTC_READ_SAMPLE_INVALID;
+
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ IPointsSchema::Sample sample;
+ schema.get(sample, ss);
+
+ P3fArraySamplePtr positions = sample.getPositions();
+
+ V3fArraySamplePtr velocities;
+ if (m_param_velocities && m_param_velocities.getNumSamples() > 0) {
+ IV3fGeomParam::Sample sample_velocities;
+ m_param_velocities.getExpanded(sample_velocities, ss);
+ velocities = sample_velocities.getVals();
+ }
+
+ P3fArraySamplePtr goal_positions;
+ if (m_param_goal_positions && m_param_goal_positions.getNumSamples() > 0) {
+ IP3fGeomParam::Sample sample_goal_positions;
+ m_param_goal_positions.getExpanded(sample_goal_positions, ss);
+ goal_positions = sample_goal_positions.getVals();
+ }
+
+ apply_sample_positions(cloth, positions);
+ if (velocities)
+ apply_sample_velocities(cloth, velocities);
+ if (goal_positions)
+ apply_sample_goal_positions(cloth, goal_positions);
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_cloth.h b/source/blender/pointcache/alembic/abc_cloth.h
new file mode 100644
index 00000000000..9b30b183a6c
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_cloth.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_CLOTH_H
+#define PTC_ABC_CLOTH_H
+
+#include <Alembic/AbcGeom/IPoints.h>
+#include <Alembic/AbcGeom/OPoints.h>
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+
+struct Object;
+struct ClothModifierData;
+
+namespace PTC {
+
+class AbcClothWriter : public ClothWriter, public AbcWriter {
+public:
+ AbcClothWriter(const std::string &name, Object *ob, ClothModifierData *clmd);
+ ~AbcClothWriter();
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ AbcGeom::OPoints m_points;
+ AbcGeom::OV3fGeomParam m_param_velocities;
+ AbcGeom::OP3fGeomParam m_param_goal_positions;
+};
+
+class AbcClothReader : public ClothReader, public AbcReader {
+public:
+ AbcClothReader(const std::string &name, Object *ob, ClothModifierData *clmd);
+ ~AbcClothReader();
+
+ void init_abc(Abc::IObject parent);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ AbcGeom::IPoints m_points;
+ AbcGeom::IV3fGeomParam m_param_velocities;
+ AbcGeom::IP3fGeomParam m_param_goal_positions;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_CLOTH_H */
diff --git a/source/blender/pointcache/alembic/abc_customdata.cpp b/source/blender/pointcache/alembic/abc_customdata.cpp
new file mode 100644
index 00000000000..77a9ea73a5b
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_customdata.cpp
@@ -0,0 +1,804 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include <sstream>
+
+#include <Alembic/AbcGeom/IGeomParam.h>
+#include <Alembic/AbcGeom/OGeomParam.h>
+
+#include "abc_customdata.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_customdata_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+/* DEBUG */
+BLI_INLINE void print_writer_compound(OCompoundProperty &prop)
+{
+ CompoundPropertyWriterPtr ptr = prop.getPtr()->asCompoundPtr();
+ printf("compound %s: [%p] (%d)\n", ptr->getName().c_str(), ptr.get(), (int)ptr->getNumProperties());
+ for (int i = 0; i < ptr->getNumProperties(); ++i) {
+ printf(" %d: [%p]\n", i, prop.getProperty(i).getPtr().get());
+ printf(" %s\n", prop.getProperty(i).getName().c_str());
+ }
+}
+
+/* ========================================================================= */
+
+template <CustomDataType CDTYPE>
+static void write_sample(CustomDataWriter */*writer*/, OCompoundProperty &/*parent*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ /* no implementation available, should not happen */
+ printf("ERROR: CustomData type %s has no write_sample implementation\n", CustomData_layertype_name((int)CDTYPE));
+}
+
+template <>
+void write_sample<CD_MDEFORMVERT>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OInt32ArrayProperty totweight_prop = writer->add_array_property<OInt32ArrayProperty>(name + ":totweight", prop);
+ OInt32ArrayProperty flag_prop = writer->add_array_property<OInt32ArrayProperty>(name + ":flag", prop);
+ OInt32ArrayProperty def_nr_prop = writer->add_array_property<OInt32ArrayProperty>(name + ":def_nr", prop);
+ OFloatArrayProperty weight_prop = writer->add_array_property<OFloatArrayProperty>(name + ":weight", prop);
+
+ MDeformVert *mdef = (MDeformVert *)data;
+
+ /* sum all totweight for the sample size */
+ int num_mdefweight = 0;
+ for (int i = 0; i < num_data; ++i)
+ num_mdefweight += mdef[i].totweight;
+
+ std::vector<int32_t> totweight_data;
+ std::vector<int32_t> flag_data;
+ std::vector<int32_t> def_nr_data;
+ std::vector<float> weight_data;
+ totweight_data.reserve(num_data);
+ flag_data.reserve(num_data);
+ def_nr_data.reserve(num_mdefweight);
+ weight_data.reserve(num_mdefweight);
+
+ for (int i = 0; i < num_data; ++i) {
+ totweight_data.push_back(mdef->totweight);
+ flag_data.push_back(mdef->flag);
+
+ MDeformWeight *mw = mdef->dw;
+ for (int j = 0; j < mdef->totweight; ++j) {
+ def_nr_data.push_back(mw->def_nr);
+ weight_data.push_back(mw->weight);
+
+ ++mw;
+ }
+
+ ++mdef;
+ }
+
+ totweight_prop.set(Int32ArraySample(totweight_data));
+ flag_prop.set(Int32ArraySample(flag_data));
+ def_nr_prop.set(Int32ArraySample(def_nr_data));
+ weight_prop.set(FloatArraySample(weight_data));
+}
+
+template <>
+void write_sample<CD_MTFACE>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void */*data*/, int /*num_data*/)
+{
+ /* XXX this is a dummy layer, to have access to active render layers etc. */
+ writer->add_compound_property<OCompoundProperty>(name, parent);
+}
+
+template <>
+void write_sample<CD_MCOL>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OC4fArrayProperty prop = writer->add_array_property<OC4fArrayProperty>(name, parent);
+
+ MCol *mcol = (MCol *)data;
+
+ std::vector<C4f> mcol_data;
+ mcol_data.reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ unsigned char icol[4] = {mcol->r, mcol->g, mcol->b, mcol->a};
+ C4f fcol;
+ rgba_uchar_to_float(fcol.getValue(), icol);
+ mcol_data.push_back(fcol);
+
+ ++mcol;
+ }
+ prop.set(OC4fArrayProperty::sample_type(mcol_data));
+}
+
+template <>
+void write_sample<CD_ORIGINDEX>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OInt32ArrayProperty prop = writer->add_array_property<OInt32ArrayProperty>(name, parent);
+
+ prop.set(OInt32ArrayProperty::sample_type((int *)data, num_data));
+}
+
+template <>
+void write_sample<CD_NORMAL>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ ON3fArrayProperty prop = writer->add_array_property<ON3fArrayProperty>(name, parent);
+
+ prop.set(ON3fArrayProperty::sample_type((N3f *)data, num_data));
+}
+
+template <>
+void write_sample<CD_ORIGSPACE>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OV2fArrayProperty uv_prop[4];
+ uv_prop[0] = writer->add_array_property<OV2fArrayProperty>(name + ":uv0", prop);
+ uv_prop[1] = writer->add_array_property<OV2fArrayProperty>(name + ":uv1", prop);
+ uv_prop[2] = writer->add_array_property<OV2fArrayProperty>(name + ":uv2", prop);
+ uv_prop[3] = writer->add_array_property<OV2fArrayProperty>(name + ":uv3", prop);
+
+ OrigSpaceFace *ospace = (OrigSpaceFace *)data;
+ std::vector<V2f> uv_data[4];
+ uv_data[0].reserve(num_data);
+ uv_data[1].reserve(num_data);
+ uv_data[2].reserve(num_data);
+ uv_data[3].reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ uv_data[0].push_back(V2f(ospace->uv[0][0], ospace->uv[0][1]));
+ uv_data[1].push_back(V2f(ospace->uv[1][0], ospace->uv[1][1]));
+ uv_data[2].push_back(V2f(ospace->uv[2][0], ospace->uv[2][1]));
+ uv_data[3].push_back(V2f(ospace->uv[3][0], ospace->uv[3][1]));
+
+ ++ospace;
+ }
+ uv_prop[0].set(V2fArraySample(uv_data[0]));
+ uv_prop[1].set(V2fArraySample(uv_data[1]));
+ uv_prop[2].set(V2fArraySample(uv_data[2]));
+ uv_prop[3].set(V2fArraySample(uv_data[3]));
+}
+
+template <>
+void write_sample<CD_ORCO>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OV3fArrayProperty prop = writer->add_array_property<OV3fArrayProperty>(name, parent);
+
+ prop.set(OV3fArrayProperty::sample_type((V3f *)data, num_data));
+}
+
+template <>
+void write_sample<CD_MTEXPOLY>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void */*data*/, int /*num_data*/)
+{
+ /* XXX this is a dummy layer, to have access to active render layers etc. */
+ writer->add_compound_property<OCompoundProperty>(name, parent);
+}
+
+template <>
+void write_sample<CD_MLOOPUV>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OV2fArrayProperty prop_uv = writer->add_array_property<OV2fArrayProperty>(name + ":uv", prop);
+ OInt32ArrayProperty prop_flag = writer->add_array_property<OInt32ArrayProperty>(name + ":flag", prop);
+
+ MLoopUV *loop_uv = (MLoopUV *)data;
+ std::vector<V2f> uv_data;
+ std::vector<int32_t> flag_data;
+ uv_data.reserve(num_data);
+ flag_data.reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ uv_data.push_back(V2f(loop_uv->uv[0], loop_uv->uv[1]));
+ flag_data.push_back(loop_uv->flag);
+
+ ++loop_uv;
+ }
+ prop_uv.set(V2fArraySample(uv_data));
+ prop_flag.set(Int32ArraySample(flag_data));
+}
+
+template <>
+void write_sample<CD_MLOOPCOL>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OC4fArrayProperty prop_col = writer->add_array_property<OC4fArrayProperty>(name + ":color", prop);
+
+ MLoopCol *loop_col = (MLoopCol *)data;
+ std::vector<C4f> col_data;
+ col_data.reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ col_data.push_back(C4f(loop_col->r, loop_col->g, loop_col->b, loop_col->a));
+
+ ++loop_col;
+ }
+ prop_col.set(C4fArraySample(col_data));
+}
+
+template <>
+void write_sample<CD_ORIGSPACE_MLOOP>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OV2fArrayProperty prop_uv = writer->add_array_property<OV2fArrayProperty>(name + ":uv", prop);
+
+ OrigSpaceLoop *ospaceloop = (OrigSpaceLoop *)data;
+ std::vector<V2f> uv_data;
+ uv_data.reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ uv_data.push_back(V2f(ospaceloop->uv[0], ospaceloop->uv[1]));
+
+ ++ospaceloop;
+ }
+ prop_uv.set(V2fArraySample(uv_data));
+}
+
+template <>
+void write_sample<CD_MSURFACE_SAMPLE>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OUInt32ArrayProperty prop_orig_verts = writer->add_array_property<OUInt32ArrayProperty>(name + ":orig_verts", prop);
+ OFloatArrayProperty prop_orig_weights = writer->add_array_property<OFloatArrayProperty>(name + ":orig_weights", prop);
+ OInt32ArrayProperty prop_orig_poly = writer->add_array_property<OInt32ArrayProperty>(name + ":orig_poly", prop);
+ OUInt32ArrayProperty prop_orig_loops = writer->add_array_property<OUInt32ArrayProperty>(name + ":orig_loops", prop);
+
+ MSurfaceSample *surf = (MSurfaceSample *)data;
+ std::vector<uint32_t> orig_verts_data;
+ std::vector<float32_t> orig_weights_data;
+ std::vector<int32_t> orig_poly_data;
+ std::vector<uint32_t> orig_loops_data;
+ orig_verts_data.reserve(num_data * 3);
+ orig_weights_data.reserve(num_data * 3);
+ orig_poly_data.reserve(num_data);
+ orig_loops_data.reserve(num_data * 3);
+ for (int i = 0; i < num_data; ++i) {
+ orig_verts_data.push_back(surf->orig_verts[0]);
+ orig_verts_data.push_back(surf->orig_verts[1]);
+ orig_verts_data.push_back(surf->orig_verts[2]);
+ orig_weights_data.push_back(surf->orig_weights[0]);
+ orig_weights_data.push_back(surf->orig_weights[1]);
+ orig_weights_data.push_back(surf->orig_weights[2]);
+ orig_poly_data.push_back(surf->orig_poly);
+ orig_loops_data.push_back(surf->orig_loops[0]);
+ orig_loops_data.push_back(surf->orig_loops[1]);
+ orig_loops_data.push_back(surf->orig_loops[2]);
+
+ ++surf;
+ }
+ prop_orig_verts.set(UInt32ArraySample(orig_verts_data));
+ prop_orig_weights.set(FloatArraySample(orig_weights_data));
+ prop_orig_poly.set(Int32ArraySample(orig_poly_data));
+ prop_orig_loops.set(UInt32ArraySample(orig_loops_data));
+}
+
+/* ------------------------------------------------------------------------- */
+
+template <CustomDataType CDTYPE>
+static PTCReadSampleResult read_sample(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ /* no implementation available, should not happen */
+ printf("ERROR: CustomData type %s has no read_sample implementation\n", CustomData_layertype_name((int)CDTYPE));
+ return PTC_READ_SAMPLE_INVALID;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MDEFORMVERT>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IInt32ArrayProperty totweight_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":totweight", prop);
+ IInt32ArrayProperty flag_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":flag", prop);
+ IInt32ArrayProperty def_nr_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":def_nr", prop);
+ IFloatArrayProperty weight_prop = reader->add_array_property<IFloatArrayProperty>(name + ":weight", prop);
+
+ Int32ArraySamplePtr sample_totweight = totweight_prop.getValue(ss);
+ Int32ArraySamplePtr sample_flag = flag_prop.getValue(ss);
+ Int32ArraySamplePtr sample_def_nr = def_nr_prop.getValue(ss);
+ FloatArraySamplePtr sample_weight = weight_prop.getValue(ss);
+
+ if (sample_totweight->size() != num_data ||
+ sample_flag->size() != num_data)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ const int32_t *data_totweight = (const int32_t *)sample_totweight->getData();
+ const int32_t *data_flag = (const int32_t *)sample_flag->getData();
+ const int32_t *data_def_nr = (const int32_t *)sample_def_nr->getData();
+ const float *data_weight = (const float *)sample_weight->getData();
+
+ MDeformVert *mdef = (MDeformVert *)data;
+ for (int i = 0; i < num_data; ++i) {
+
+ mdef->totweight = *data_totweight;
+ mdef->flag = *data_flag;
+
+ MDeformWeight *mw = mdef->dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * mdef->totweight, "deformWeight");
+ for (int j = 0; j < mdef->totweight; ++j) {
+ mw->def_nr = *data_def_nr;
+ mw->weight = *data_weight;
+
+ ++data_def_nr;
+ ++data_weight;
+ ++mw;
+ }
+
+ ++data_totweight;
+ ++data_flag;
+ ++mdef;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MTFACE>(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ /* XXX this is a dummy layer, to have access to active render layers etc. */
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MCOL>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ IC4fArrayProperty prop = reader->add_array_property<IC4fArrayProperty>(name, parent);
+
+ C4fArraySamplePtr sample = prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ MCol *mcol = (MCol *)data;
+ C4f *data_mcol = (C4f *)sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ unsigned char icol[4];
+ rgba_float_to_uchar(icol, data_mcol->getValue());
+ mcol->r = icol[0];
+ mcol->g = icol[1];
+ mcol->b = icol[2];
+ mcol->a = icol[3];
+
+ ++data_mcol;
+ ++mcol;
+ }
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_ORIGINDEX>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ IInt32ArrayProperty prop = reader->add_array_property<IInt32ArrayProperty>(name, parent);
+
+ Int32ArraySamplePtr sample = prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ memcpy(data, sample->getData(), sizeof(int32_t) * num_data);
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_NORMAL>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ IN3fArrayProperty prop = reader->add_array_property<IN3fArrayProperty>(name, parent);
+
+ N3fArraySamplePtr sample = prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ memcpy(data, sample->getData(), sizeof(N3f) * num_data);
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_ORIGSPACE>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IV2fArrayProperty uv_prop[4];
+ uv_prop[0] = reader->add_array_property<IV2fArrayProperty>(name + ":uv0", prop);
+ uv_prop[1] = reader->add_array_property<IV2fArrayProperty>(name + ":uv1", prop);
+ uv_prop[2] = reader->add_array_property<IV2fArrayProperty>(name + ":uv2", prop);
+ uv_prop[3] = reader->add_array_property<IV2fArrayProperty>(name + ":uv3", prop);
+
+ V2fArraySamplePtr sample0 = uv_prop[0].getValue(ss);
+ V2fArraySamplePtr sample1 = uv_prop[1].getValue(ss);
+ V2fArraySamplePtr sample2 = uv_prop[2].getValue(ss);
+ V2fArraySamplePtr sample3 = uv_prop[3].getValue(ss);
+
+ if (sample0->size() != num_data ||
+ sample1->size() != num_data ||
+ sample2->size() != num_data ||
+ sample3->size() != num_data)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ OrigSpaceFace *ospace = (OrigSpaceFace *)data;
+ const V2f *data0 = (const V2f *)sample0->getData();
+ const V2f *data1 = (const V2f *)sample1->getData();
+ const V2f *data2 = (const V2f *)sample2->getData();
+ const V2f *data3 = (const V2f *)sample3->getData();
+ for (int i = 0; i < num_data; ++i) {
+ copy_v2_v2(ospace->uv[0], data0->getValue());
+ copy_v2_v2(ospace->uv[1], data1->getValue());
+ copy_v2_v2(ospace->uv[2], data2->getValue());
+ copy_v2_v2(ospace->uv[3], data3->getValue());
+
+ ++data0;
+ ++data1;
+ ++data2;
+ ++data3;
+ ++ospace;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_ORCO>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ IV3fArrayProperty prop = reader->add_array_property<IV3fArrayProperty>(name, parent);
+
+ V3fArraySamplePtr sample = prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ memcpy(data, sample->getData(), sizeof(V3f) * num_data);
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MTEXPOLY>(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ /* XXX this is a dummy layer, to have access to active render layers etc. */
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MLOOPUV>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IV2fArrayProperty uv_prop = reader->add_array_property<IV2fArrayProperty>(name + ":uv", prop);
+ IInt32ArrayProperty flag_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":flag", prop);
+
+ V2fArraySamplePtr uv_sample = uv_prop.getValue(ss);
+ Int32ArraySamplePtr flag_sample = flag_prop.getValue(ss);
+
+ if (uv_sample->size() != num_data || flag_sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ MLoopUV *loop_uv = (MLoopUV *)data;
+ const V2f *uv_data = (const V2f *)uv_sample->getData();
+ const int32_t *flag_data = (const int32_t *)flag_sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ copy_v2_v2(loop_uv->uv, uv_data->getValue());
+ loop_uv->flag = *flag_data;
+
+ ++uv_data;
+ ++flag_data;
+ ++loop_uv;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MLOOPCOL>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IC4fArrayProperty col_prop = reader->add_array_property<IC4fArrayProperty>(name + ":color", prop);
+
+ C4fArraySamplePtr col_sample = col_prop.getValue(ss);
+
+ if (col_sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ MLoopCol *loop_col = (MLoopCol *)data;
+ const C4f *col_data = (const C4f *)col_sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ loop_col->r = col_data->r;
+ loop_col->g = col_data->g;
+ loop_col->b = col_data->b;
+ loop_col->a = col_data->a;
+
+ ++col_data;
+ ++loop_col;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_ORIGSPACE_MLOOP>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IV2fArrayProperty uv_prop = reader->add_array_property<IV2fArrayProperty>(name + ":uv", prop);
+
+ V2fArraySamplePtr sample = uv_prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ OrigSpaceLoop *ospace = (OrigSpaceLoop *)data;
+ const V2f *sample_data = (const V2f *)sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ copy_v2_v2(ospace->uv, sample_data->getValue());
+
+ ++sample_data;
+ ++ospace;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MSURFACE_SAMPLE>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IUInt32ArrayProperty orig_verts_prop = reader->add_array_property<IUInt32ArrayProperty>(name + ":orig_verts", prop);
+ IFloatArrayProperty orig_weights_prop = reader->add_array_property<IFloatArrayProperty>(name + ":orig_weights", prop);
+ IInt32ArrayProperty orig_poly_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":orig_poly", prop);
+ IUInt32ArrayProperty orig_loops_prop = reader->add_array_property<IUInt32ArrayProperty>(name + ":orig_loops", prop);
+
+ UInt32ArraySamplePtr orig_verts_sample = orig_verts_prop.getValue(ss);
+ FloatArraySamplePtr orig_weights_sample = orig_weights_prop.getValue(ss);
+ Int32ArraySamplePtr orig_poly_sample = orig_poly_prop.getValue(ss);
+ UInt32ArraySamplePtr orig_loops_sample = orig_loops_prop.getValue(ss);
+
+ if (orig_verts_sample->size() != num_data*3 ||
+ orig_weights_sample->size() != num_data*3 ||
+ orig_poly_sample->size() != num_data ||
+ orig_loops_sample->size() != num_data*3)
+ return PTC_READ_SAMPLE_INVALID;
+
+ MSurfaceSample *surf = (MSurfaceSample *)data;
+ const uint32_t *orig_verts_data = (const uint32_t *)orig_verts_sample->getData();
+ const float32_t *orig_weights_data = (const float32_t *)orig_weights_sample->getData();
+ const int32_t *orig_poly_data = (const int32_t *)orig_poly_sample->getData();
+ const uint32_t *orig_loops_data = (const uint32_t *)orig_loops_sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ surf->orig_verts[0] = orig_verts_data[0];
+ surf->orig_verts[1] = orig_verts_data[1];
+ surf->orig_verts[2] = orig_verts_data[2];
+ surf->orig_weights[0] = orig_weights_data[0];
+ surf->orig_weights[1] = orig_weights_data[1];
+ surf->orig_weights[2] = orig_weights_data[2];
+ surf->orig_poly = *orig_poly_data;
+ surf->orig_loops[0] = orig_loops_data[0];
+ surf->orig_loops[1] = orig_loops_data[1];
+ surf->orig_loops[2] = orig_loops_data[2];
+
+ orig_verts_data += 3;
+ orig_weights_data += 3;
+ orig_poly_data += 1;
+ orig_loops_data += 3;
+ ++surf;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+/* ========================================================================= */
+
+/* recursive template that handles dispatch by CD layer type */
+template <int CDTYPE>
+BLI_INLINE void write_sample_call(CustomDataWriter *writer, OCompoundProperty &parent, CustomDataType type, const std::string &name, void *data, int num_data)
+{
+ if (type == CDTYPE)
+ write_sample<(CustomDataType)CDTYPE>(writer, parent, name, data, num_data);
+ else
+ write_sample_call<CDTYPE + 1>(writer, parent, type, name, data, num_data);
+}
+
+/* terminator specialization */
+template <>
+void write_sample_call<CD_NUMTYPES>(CustomDataWriter */*writer*/, OCompoundProperty &/*parent*/, CustomDataType /*type*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* recursive template that handles dispatch by CD layer type */
+template <int CDTYPE>
+BLI_INLINE PTCReadSampleResult read_sample_call(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, CustomDataType type, const std::string &name, void *data, int num_data)
+{
+ if (type == CDTYPE)
+ return read_sample<(CustomDataType)CDTYPE>(reader, parent, ss, name, data, num_data);
+ else
+ return read_sample_call<CDTYPE + 1>(reader, parent, ss, type, name, data, num_data);
+}
+
+/* terminator specialization */
+template <>
+PTCReadSampleResult read_sample_call<CD_NUMTYPES>(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, CustomDataType /*type*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ return PTC_READ_SAMPLE_INVALID;
+}
+
+/* ========================================================================= */
+
+CustomDataWriter::CustomDataWriter(const std::string &name, CustomDataMask cdmask) :
+ m_name(name),
+ m_cdmask(cdmask)
+{
+}
+
+CustomDataWriter::~CustomDataWriter()
+{
+ for (LayerPropsMap::iterator it = m_layer_props.begin(); it != m_layer_props.end(); ++it) {
+ BasePropertyWriterPtr prop = it->second;
+ if (prop)
+ prop.reset();
+ }
+}
+
+void CustomDataWriter::init(TimeSamplingPtr time_sampling)
+{
+ m_time_sampling = time_sampling;
+}
+
+/* unique property name based on either layer name or index */
+std::string CustomDataWriter::cdtype_to_name(CustomData *cdata, CustomDataType type, int n)
+{
+ const char *layertype_name = CustomData_layertype_name(type);
+ const char *layer_name = CustomData_get_layer_name(cdata, type, n);
+ std::string name;
+ if (layer_name && layer_name[0] != '\0') {
+ name = m_name + ":" + std::string(layertype_name) + ":S" + std::string(layer_name);
+ }
+ else {
+ std::stringstream ss; ss << n;
+ name = m_name + ":" + std::string(layertype_name) + ":N" + ss.str();
+ }
+ return name;
+}
+
+/* parse property name to CD layer name based on S or N prefix for named/unnamed layers */
+void CustomDataReader::cdtype_from_name(CustomData */*cdata*/, const std::string &name, int type, int *n, char *layer_name, int max_layer_name)
+{
+ const char *layertype_name = CustomData_layertype_name(type);
+ /* We can safely assume all properties in the compound share the correct prefix
+ * <m_name>:<layertype_name>:
+ * The layertype_name is only prepended to avoid name collisions
+ */
+ const size_t start = m_name.size() + 1 + strlen(layertype_name) + 1;
+
+ if (name.size() <= start) {
+ printf("ERROR: invalid CustomData layer property name '%s'\n", name.c_str());
+ *n = -1;
+ layer_name[0] = '\0';
+ }
+ else if (name[start] == 'S') {
+ /* named layer */
+ *n = -1;
+ BLI_strncpy(layer_name, name.c_str() + start + 1, max_layer_name);
+ }
+ else if (name[start] == 'N') {
+ /* unnamed layer */
+ std::istringstream ss(name.c_str() + start + 1);
+ ss >> (*n);
+ layer_name[0] = '\0';
+ }
+ else {
+ *n = -1;
+ layer_name[0] = '\0';
+ }
+}
+
+void CustomDataWriter::write_sample(CustomData *cdata, int num_data, OCompoundProperty &parent)
+{
+ /* compound property for all CD layers in the CustomData instance */
+ m_props = add_compound_property<OCompoundProperty>(m_name, parent);
+
+ for (int type = 0; type < CD_NUMTYPES; ++type) {
+ CustomDataMask mask = (1ull << type);
+ /* only use specified types */
+ if (!(mask & m_cdmask))
+ continue;
+
+ const char *layertype_name = CustomData_layertype_name(type);
+ int num = CustomData_number_of_layers(cdata, type);
+
+ bool has_props = false;
+ OCompoundProperty layertype_props;
+ for (int n = 0; n < num; ++n) {
+ /* compound for all CD layers of the same type */
+ if (!has_props) {
+ has_props = true;
+ layertype_props = add_compound_property<OCompoundProperty>(m_name + ":" + layertype_name, m_props);
+ }
+
+ std::string name = cdtype_to_name(cdata, (CustomDataType)type, n);
+ void *data = CustomData_get_layer_n(cdata, type, n);
+ write_sample_call<0>(this, layertype_props, (CustomDataType)type, name, data, num_data);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+CustomDataReader::CustomDataReader(const std::string &name, CustomDataMask cdmask) :
+ m_name(name),
+ m_cdmask(cdmask)
+{
+}
+
+CustomDataReader::~CustomDataReader()
+{
+ for (LayerPropsMap::iterator it = m_layer_props.begin(); it != m_layer_props.end(); ++it) {
+ BasePropertyReaderPtr prop = it->second;
+ if (prop)
+ prop.reset();
+ }
+}
+
+PTCReadSampleResult CustomDataReader::read_sample(const ISampleSelector &ss, CustomData *cdata, int num_data, ICompoundProperty &parent)
+{
+ m_props = add_compound_property<ICompoundProperty>(m_name, parent);
+
+ for (int type = 0; type < CD_NUMTYPES; ++type) {
+ CustomDataMask mask = (1ull << type);
+ /* only use specified types */
+ if (!(mask & m_cdmask))
+ continue;
+
+ const char *layertype_name = CustomData_layertype_name(type);
+
+ BasePropertyReaderPtr ptr = m_props.getPtr()->asCompoundPtr()->getProperty(m_name + ":" + layertype_name);
+ if (!ptr) {
+ /* no layer of this type stored */
+ continue;
+ }
+ ICompoundProperty layertype_props(ptr->asCompoundPtr(), kWrapExisting);
+
+ for (int i = 0; i < layertype_props.getNumProperties(); ++i) {
+ const std::string &name = layertype_props.getPropertyHeader(i).getName();
+ char layer_name[MAX_CUSTOMDATA_LAYER_NAME];
+ int n;
+ void *data;
+
+ cdtype_from_name(cdata, name, type, &n, layer_name, sizeof(layer_name));
+ if (layer_name[0] == '\0')
+ data = CustomData_add_layer(cdata, type, CD_DEFAULT, NULL, num_data);
+ else
+ data = CustomData_add_layer_named(cdata, type, CD_DEFAULT, NULL, num_data, layer_name);
+
+ read_sample_call<0>(this, layertype_props, ss, (CustomDataType)type, name, data, num_data);
+ }
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_customdata.h b/source/blender/pointcache/alembic/abc_customdata.h
new file mode 100644
index 00000000000..9da3b92c584
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_customdata.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_CUSTOMDATA_H
+#define PTC_ABC_CUSTOMDATA_H
+
+#include <map>
+
+#include <Alembic/AbcGeom/IGeomParam.h>
+#include <Alembic/AbcGeom/OGeomParam.h>
+#include <Alembic/Abc/IBaseProperty.h>
+#include <Alembic/Abc/TypedPropertyTraits.h>
+
+#include "abc_reader.h"
+#include "abc_writer.h"
+
+extern "C" {
+#include "BKE_customdata.h"
+
+#include "DNA_customdata_types.h"
+}
+
+namespace PTC {
+
+using namespace Alembic;
+
+std::string abc_customdata_layer_name(CustomData *cdata, CustomDataType type, int n);
+
+struct CustomDataWriter {
+ typedef std::map<std::string, Abc::BasePropertyWriterPtr> LayerPropsMap;
+ typedef std::pair<std::string, Abc::BasePropertyWriterPtr> LayerPropsPair;
+
+ CustomDataWriter(const std::string &name, CustomDataMask cdmask);
+ ~CustomDataWriter();
+
+ void init(Abc::TimeSamplingPtr time_sampling);
+
+ void write_sample(CustomData *cdata, int num_data, Abc::OCompoundProperty &parent);
+
+ Abc::OCompoundProperty &props() { return m_props; }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_scalar_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, m_time_sampling);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asScalarPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_array_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, m_time_sampling);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asArrayPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_compound_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, m_time_sampling);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asCompoundPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ std::string cdtype_to_name(CustomData *cdata, CustomDataType type, int n);
+
+private:
+ std::string m_name;
+ CustomDataMask m_cdmask;
+
+ Abc::TimeSamplingPtr m_time_sampling;
+ Abc::OCompoundProperty m_props;
+ LayerPropsMap m_layer_props;
+};
+
+struct CustomDataReader {
+ typedef std::map<std::string, Abc::BasePropertyReaderPtr> LayerPropsMap;
+ typedef std::pair<std::string, Abc::BasePropertyReaderPtr> LayerPropsPair;
+
+ CustomDataReader(const std::string &name, CustomDataMask cdmask);
+ ~CustomDataReader();
+
+ PTCReadSampleResult read_sample(const Abc::ISampleSelector &ss, CustomData *cdata, int num_data, Abc::ICompoundProperty &parent);
+
+ Abc::ICompoundProperty &props() { return m_props; }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_scalar_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, 0);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asScalarPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_array_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, 0);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asArrayPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_compound_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, 0);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asCompoundPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ void cdtype_from_name(CustomData *cdata, const std::string &name, int type, int *n, char *layer_name, int max_layer_name);
+
+private:
+ std::string m_name;
+ CustomDataMask m_cdmask;
+
+ Abc::ICompoundProperty m_props;
+ LayerPropsMap m_layer_props;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_CLOTH_H */
diff --git a/source/blender/pointcache/alembic/abc_frame_mapper.cpp b/source/blender/pointcache/alembic/abc_frame_mapper.cpp
new file mode 100644
index 00000000000..ba5ded10b6e
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_frame_mapper.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "abc_frame_mapper.h"
+
+extern "C" {
+#include "DNA_scene_types.h"
+}
+
+namespace PTC {
+
+#ifdef WITH_ALEMBIC
+
+using namespace Abc;
+using namespace AbcCoreAbstract;
+
+FrameMapper::FrameMapper(double fps, float start_frame)
+{
+ m_frames_per_sec = fps;
+ m_sec_per_frame = (fps == 0.0 ? 0.0 : 1.0 / fps);
+ m_start_frame = start_frame;
+ m_start_time = start_frame * m_sec_per_frame;
+}
+
+chrono_t FrameMapper::frame_to_time(float frame) const
+{
+ return (double)frame * m_sec_per_frame;
+}
+
+float FrameMapper::time_to_frame(chrono_t time) const
+{
+ return (float)(time * m_frames_per_sec);
+}
+
+#endif /* WITH_ALEMBIC */
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_frame_mapper.h b/source/blender/pointcache/alembic/abc_frame_mapper.h
new file mode 100644
index 00000000000..b61baeeb4a7
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_frame_mapper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_FRAME_MAPPER_H
+#define PTC_ABC_FRAME_MAPPER_H
+
+#ifdef WITH_ALEMBIC
+#include <Alembic/AbcCoreAbstract/Foundation.h>
+#include <Alembic/Abc/ISampleSelector.h>
+#endif
+
+struct Scene;
+
+namespace PTC {
+
+#ifdef WITH_ALEMBIC
+
+using namespace Alembic;
+using Alembic::AbcCoreAbstract::chrono_t;
+
+class FrameMapper {
+public:
+ FrameMapper(double fps, float start_frame);
+
+ double frames_per_second() const { return m_frames_per_sec; }
+ double seconds_per_frame() const { return m_sec_per_frame; }
+ double start_frame() const { return m_start_frame; }
+ double start_time() const { return m_start_time; }
+
+ chrono_t frame_to_time(float frame) const;
+ float time_to_frame(chrono_t time) const;
+
+private:
+ double m_frames_per_sec, m_sec_per_frame;
+ double m_start_frame, m_start_time;
+};
+
+#endif /* WITH_ALEMBIC */
+
+} /* namespace PTC */
+
+#endif /* PTC_UTIL_FRAME_MAPPER_H */
diff --git a/source/blender/pointcache/alembic/abc_group.cpp b/source/blender/pointcache/alembic/abc_group.cpp
new file mode 100644
index 00000000000..38cbc19694e
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_group.cpp
@@ -0,0 +1,770 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <Alembic/Abc/IObject.h>
+#include <Alembic/Abc/OObject.h>
+
+#include "abc_mesh.h"
+#include "abc_group.h"
+#include "abc_interpolate.h"
+#include "abc_object.h"
+#include "abc_particles.h"
+#include "abc_simdebug.h"
+
+extern "C" {
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "DNA_group_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_anim.h"
+#include "BKE_cache_library.h"
+#include "BKE_global.h"
+#include "BKE_group.h"
+#include "BKE_library.h"
+#include "BKE_strands.h"
+}
+
+#include "util_task.h"
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+AbcGroupWriter::AbcGroupWriter(const std::string &name, Group *group) :
+ GroupWriter(group, name)
+{
+}
+
+void AbcGroupWriter::init_abc()
+{
+ if (m_abc_object)
+ return;
+
+ m_abc_object = abc_archive()->add_id_object<OObject>((ID *)m_group);
+}
+
+void AbcGroupWriter::create_refs()
+{
+ GroupObject *gob = (GroupObject *)m_group->gobject.first;
+ int i = 0;
+ for (; gob; gob = gob->next, ++i) {
+ OObject abc_object = abc_archive()->get_id_object((ID *)gob->ob);
+ if (abc_object) {
+ std::stringstream ss;
+ ss << i;
+ m_abc_object.addChildInstance(abc_object, std::string("group_object") + ss.str());
+ }
+ }
+}
+
+void AbcGroupWriter::write_sample()
+{
+ if (!m_abc_object)
+ return;
+}
+
+
+AbcGroupReader::AbcGroupReader(const std::string &name, Group *group) :
+ GroupReader(group, name)
+{
+}
+
+void AbcGroupReader::init_abc(IObject object)
+{
+ if (m_abc_object)
+ return;
+ m_abc_object = object;
+}
+
+PTCReadSampleResult AbcGroupReader::read_sample_abc(chrono_t /*time*/)
+{
+ if (!m_abc_object)
+ return PTC_READ_SAMPLE_INVALID;
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+/* ========================================================================= */
+
+AbcDupligroupWriter::AbcDupligroupWriter(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib) :
+ GroupWriter(group, name),
+ m_eval_ctx(eval_ctx),
+ m_scene(scene),
+ m_cachelib(cachelib)
+{
+}
+
+AbcDupligroupWriter::~AbcDupligroupWriter()
+{
+ for (IDWriterMap::iterator it = m_id_writers.begin(); it != m_id_writers.end(); ++it) {
+ if (it->second)
+ delete it->second;
+ }
+}
+
+void AbcDupligroupWriter::init_abc()
+{
+ if (m_abc_group)
+ return;
+
+ m_abc_group = abc_archive()->add_id_object<OObject>((ID *)m_group);
+}
+
+void AbcDupligroupWriter::write_sample_object(Object *ob, bool write_data)
+{
+ AbcWriter *ob_writer;
+
+ /* TODO(sergey): Optimize this out using RW mutex. */
+ {
+ thread_scoped_lock lock(m_init_mutex);
+ ob_writer = find_id_writer((ID *)ob);
+ if (!ob_writer) {
+ bool do_mesh = write_data && (m_cachelib->data_types & CACHE_TYPE_DERIVED_MESH);
+ bool do_hair = write_data && (m_cachelib->data_types & CACHE_TYPE_HAIR);
+
+ ob_writer = new AbcObjectWriter(ob->id.name, m_scene, ob, do_mesh, do_hair);
+ ob_writer->init(abc_archive());
+ m_id_writers.insert(IDWriterPair((ID *)ob, ob_writer));
+ }
+ }
+
+ ob_writer->write_sample();
+}
+
+void AbcDupligroupWriter::write_sample_dupli(DupliObject *dob, int index)
+{
+ OObject abc_object = abc_archive()->get_id_object((ID *)dob->ob);
+ if (!abc_object)
+ return;
+
+ std::stringstream ss;
+ ss << "DupliObject" << index;
+ std::string name = ss.str();
+
+ OObject abc_dupli = m_abc_group.getChild(name);
+ OCompoundProperty props;
+ OM44fProperty prop_matrix;
+ OBoolProperty prop_visible;
+ if (!abc_dupli) {
+ abc_dupli = OObject(m_abc_group, name, 0);
+ m_object_writers.push_back(abc_dupli.getPtr());
+ props = abc_dupli.getProperties();
+
+ abc_dupli.addChildInstance(abc_object, "object");
+
+ prop_matrix = OM44fProperty(props, "matrix", abc_archive()->frame_sampling());
+ m_property_writers.push_back(prop_matrix.getPtr());
+ prop_visible = OBoolProperty(props, "visible", abc_archive()->frame_sampling());
+ m_property_writers.push_back(prop_visible.getPtr());
+ }
+ else {
+ props = abc_dupli.getProperties();
+
+ prop_matrix = OM44fProperty(props.getProperty("matrix").getPtr()->asScalarPtr(), kWrapExisting);
+ prop_visible = OBoolProperty(props.getProperty("visible").getPtr()->asScalarPtr(), kWrapExisting);
+ }
+
+ prop_matrix.set(M44f(dob->mat));
+
+ bool show_object = (abc_archive()->use_render())? !(dob->ob->restrictflag & OB_RESTRICT_RENDER) : !(dob->ob->restrictflag & OB_RESTRICT_VIEW);
+ bool visible = show_object && (!dob->no_draw);
+ prop_visible.set(visible);
+}
+
+void AbcDupligroupWriter::write_sample()
+{
+ if (!m_abc_group)
+ return;
+
+ ListBase *duplilist = group_duplilist_ex(m_eval_ctx, m_scene, m_group, true);
+ DupliObject *dob;
+ int i;
+
+ /* use a set to ensure each object is handled only once */
+ std::set<Object*> objects;
+ for (dob = (DupliObject *)duplilist->first; dob; dob = dob->next) {
+ if (dob->ob)
+ objects.insert(dob->ob);
+ }
+
+ /* tag objects for which to store data */
+ BKE_cache_library_tag_used_objects(m_cachelib);
+
+ UtilTaskPool pool;
+ /* write actual object data: duplicator itself + all instanced objects */
+ for (std::set<Object*>::const_iterator it = objects.begin(); it != objects.end(); ++it) {
+ Object *ob = *it;
+ bool write_data = (ob->id.flag & LIB_DOIT);
+ pool.push(function_bind(&AbcDupligroupWriter::write_sample_object, this, ob, write_data));
+ }
+
+ pool.wait_work();
+
+ /* write dupli instances */
+ for (dob = (DupliObject *)duplilist->first, i = 0; dob; dob = dob->next, ++i) {
+ write_sample_dupli(dob, i);
+ }
+
+ free_object_duplilist(duplilist);
+}
+
+AbcWriter *AbcDupligroupWriter::find_id_writer(ID *id) const
+{
+ IDWriterMap::const_iterator it = m_id_writers.find(id);
+ if (it == m_id_writers.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+/* ------------------------------------------------------------------------- */
+
+AbcDupliCacheWriter::AbcDupliCacheWriter(const std::string &name, Group *group, DupliCache *dupcache, int data_types, bool do_sim_debug) :
+ GroupWriter(group, name),
+ m_dupcache(dupcache),
+ m_data_types(data_types),
+ m_simdebug_writer(NULL)
+{
+ if (do_sim_debug) {
+ BKE_sim_debug_data_set_enabled(true);
+ if (_sim_debug_data)
+ m_simdebug_writer = new AbcSimDebugWriter("sim_debug", _sim_debug_data);
+ }
+}
+
+AbcDupliCacheWriter::~AbcDupliCacheWriter()
+{
+ for (IDWriterMap::iterator it = m_id_writers.begin(); it != m_id_writers.end(); ++it) {
+ if (it->second)
+ delete it->second;
+ }
+
+ if (m_simdebug_writer)
+ delete m_simdebug_writer;
+}
+
+void AbcDupliCacheWriter::init_abc()
+{
+ if (m_abc_group)
+ return;
+
+ m_abc_group = abc_archive()->add_id_object<OObject>((ID *)m_group);
+
+ if (m_simdebug_writer) {
+ m_simdebug_writer->init(abc_archive());
+ m_simdebug_writer->init_abc(abc_archive()->root());
+ }
+}
+
+void AbcDupliCacheWriter::write_sample_object_data(DupliObjectData *data)
+{
+ AbcWriter *ob_writer = find_id_writer((ID *)data->ob);
+ if (!ob_writer) {
+ bool do_mesh = (m_data_types & CACHE_TYPE_DERIVED_MESH);
+ bool do_hair = (m_data_types & CACHE_TYPE_HAIR);
+
+ ob_writer = new AbcDupliObjectWriter(data->ob->id.name, data, do_mesh, do_hair);
+ ob_writer->init(abc_archive());
+ m_id_writers.insert(IDWriterPair((ID *)data->ob, ob_writer));
+ }
+
+ ob_writer->write_sample();
+}
+
+void AbcDupliCacheWriter::write_sample_dupli(DupliObject *dob, int index)
+{
+ OObject abc_object = abc_archive()->get_id_object((ID *)dob->ob);
+ if (!abc_object)
+ return;
+
+ std::stringstream ss;
+ ss << "DupliObject" << index;
+ std::string name = ss.str();
+
+ OObject abc_dupli = m_abc_group.getChild(name);
+ OCompoundProperty props;
+ OBoolProperty prop_visible;
+ OM44fProperty prop_matrix;
+ if (!abc_dupli) {
+ abc_dupli = OObject(m_abc_group, name, 0);
+ m_object_writers.push_back(abc_dupli.getPtr());
+ props = abc_dupli.getProperties();
+
+ abc_dupli.addChildInstance(abc_object, "object");
+
+ prop_matrix = OM44fProperty(props, "matrix", abc_archive()->frame_sampling());
+ m_property_writers.push_back(prop_matrix.getPtr());
+ prop_visible = OBoolProperty(props, "visible", abc_archive()->frame_sampling());
+ m_property_writers.push_back(prop_visible.getPtr());
+ }
+ else {
+ props = abc_dupli.getProperties();
+
+ prop_matrix = OM44fProperty(props.getProperty("matrix").getPtr()->asScalarPtr(), kWrapExisting);
+ prop_visible = OBoolProperty(props.getProperty("visible").getPtr()->asScalarPtr(), kWrapExisting);
+ }
+
+ prop_matrix.set(M44f(dob->mat));
+
+ bool show_object = (abc_archive()->use_render())? !(dob->ob->restrictflag & OB_RESTRICT_RENDER) : !(dob->ob->restrictflag & OB_RESTRICT_VIEW);
+ bool visible = show_object && (!dob->no_draw);
+ prop_visible.set(visible);
+}
+
+void AbcDupliCacheWriter::write_sample()
+{
+ if (!m_abc_group)
+ return;
+
+ DupliObject *dob;
+ int i;
+
+ struct DupliCacheIterator *iter = BKE_dupli_cache_iter_new(m_dupcache);
+ for (; BKE_dupli_cache_iter_valid(iter); BKE_dupli_cache_iter_next(iter)) {
+ DupliObjectData *data = BKE_dupli_cache_iter_get(iter);
+
+ write_sample_object_data(data);
+ }
+ BKE_dupli_cache_iter_free(iter);
+
+ /* write dupli instances */
+ for (dob = (DupliObject *)m_dupcache->duplilist.first, i = 0; dob; dob = dob->next, ++i) {
+ write_sample_dupli(dob, i);
+ }
+
+ if (m_simdebug_writer) {
+ m_simdebug_writer->write_sample();
+ }
+}
+
+AbcWriter *AbcDupliCacheWriter::find_id_writer(ID *id) const
+{
+ IDWriterMap::const_iterator it = m_id_writers.find(id);
+ if (it == m_id_writers.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+/* ------------------------------------------------------------------------- */
+
+AbcDupliCacheReader::AbcDupliCacheReader(const std::string &name, Group *group, DupliCache *dupli_cache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug) :
+ GroupReader(group, name),
+ dupli_cache(dupli_cache),
+ m_read_strands_motion(read_strands_motion),
+ m_read_strands_children(read_strands_children),
+ m_simdebug_reader(NULL)
+{
+ /* XXX this mapping allows fast lookup of existing objects in Blender data
+ * to associate with duplis. Later i may be possible to create instances of
+ * non-DNA data, but for the time being this is a requirement due to other code parts (drawing, rendering)
+ */
+ build_object_map(G.main, group);
+
+ if (read_sim_debug) {
+ BKE_sim_debug_data_set_enabled(true);
+ if (_sim_debug_data)
+ m_simdebug_reader = new AbcSimDebugReader(_sim_debug_data);
+ }
+}
+
+AbcDupliCacheReader::~AbcDupliCacheReader()
+{
+ if (m_simdebug_reader)
+ delete m_simdebug_reader;
+}
+
+void AbcDupliCacheReader::init_abc(IObject /*object*/)
+{
+}
+
+void AbcDupliCacheReader::read_dupligroup_object(IObject object, chrono_t time)
+{
+ if (GS(object.getName().c_str()) == ID_OB) {
+ /* instances are handled later, we create true object data here */
+ if (object.isInstanceDescendant())
+ return;
+
+ Object *b_ob = find_object(object.getName());
+ if (!b_ob)
+ return;
+
+ /* Always add dupli data, even if no geometry is stored.
+ * Any missing geometry data will be replaced by the original uncached data in drawing/rendering if available.
+ */
+ DupliObjectData *dupli_data = BKE_dupli_cache_add_object(dupli_cache, b_ob);
+ insert_dupli_data(object.getPtr(), dupli_data);
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ IObject child = object.getChild(i);
+ const MetaData &metadata = child.getMetaData();
+
+ if (IPolyMeshSchema::matches(metadata)) {
+ AbcDerivedMeshReader dm_reader("mesh", b_ob);
+ dm_reader.init(abc_archive());
+ dm_reader.init_abc(child);
+ if (dm_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) {
+ BKE_dupli_object_data_set_mesh(dupli_data, dm_reader.acquire_result());
+ }
+ else {
+ dm_reader.discard_result();
+ }
+ }
+ else if (ICurvesSchema::matches(metadata)) {
+ Strands *strands;
+ StrandsChildren *children;
+ BKE_dupli_object_data_find_strands(dupli_data, child.getName().c_str(), &strands, &children);
+
+ AbcStrandsReader strands_reader(strands, children, m_read_strands_motion, m_read_strands_children);
+ strands_reader.init(abc_archive());
+ strands_reader.init_abc(child);
+ if (strands_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) {
+ Strands *newstrands = strands_reader.acquire_result();
+ if (strands && strands != newstrands) {
+ /* reader can replace strands internally if topology does not match */
+ BKE_strands_free(strands);
+ }
+ BKE_dupli_object_data_add_strands(dupli_data, child.getName().c_str(), newstrands);
+
+ StrandsChildren *newchildren = strands_reader.child_reader().acquire_result();
+ if (children && children != newchildren) {
+ /* reader can replace strands internally if topology does not match */
+ BKE_strands_children_free(children);
+ }
+ BKE_dupli_object_data_add_strands_children(dupli_data, child.getName().c_str(), newchildren);
+ }
+ else {
+ strands_reader.discard_result();
+ strands_reader.child_reader().discard_result();
+ }
+ }
+ }
+ }
+}
+
+void AbcDupliCacheReader::read_dupligroup_group(IObject abc_group, chrono_t time)
+{
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ if (GS(abc_group.getName().c_str()) == ID_GR) {
+ size_t num_child = abc_group.getNumChildren();
+
+ for (size_t i = 0; i < num_child; ++i) {
+ IObject abc_dupli = abc_group.getChild(i);
+ ICompoundProperty props = abc_dupli.getProperties();
+
+ IM44fProperty prop_matrix(props, "matrix", 0);
+ M44f abc_matrix = abc_interpolate_sample_linear(prop_matrix, time);
+ float matrix[4][4];
+ memcpy(matrix, abc_matrix.getValue(), sizeof(matrix));
+
+ IBoolProperty prop_visible(props, "visible", 0);
+ bool visible = prop_visible.getValue(ss);
+
+ IObject abc_dupli_object = abc_dupli.getChild("object");
+ if (abc_dupli_object.isInstanceRoot()) {
+ DupliObjectData *dupli_data = find_dupli_data(abc_dupli_object.getPtr());
+ if (dupli_data) {
+ DupliObject *dob = BKE_dupli_cache_add_instance(dupli_cache, matrix, dupli_data);
+ dob->no_draw = !visible;
+ }
+ }
+ }
+ }
+}
+
+PTCReadSampleResult AbcDupliCacheReader::read_sample_abc(chrono_t time)
+{
+ IObject abc_top = abc_archive()->root();
+ IObject abc_group = abc_archive()->get_id_object((ID *)m_group);
+ if (!abc_group)
+ return PTC_READ_SAMPLE_INVALID;
+
+ /* first create shared object data */
+ for (size_t i = 0; i < abc_top.getNumChildren(); ++i) {
+ read_dupligroup_object(abc_top.getChild(i), time);
+ }
+
+ BKE_dupli_cache_clear_instances(dupli_cache);
+
+ /* now generate dupli instances for the group */
+ read_dupligroup_group(abc_group, time);
+
+ // XXX reader init is a mess ...
+ if (m_simdebug_reader) {
+ if (abc_top.getChildHeader("sim_debug")) {
+ m_simdebug_reader->init(abc_archive());
+ m_simdebug_reader->init_abc(abc_top.getChild("sim_debug"));
+
+ m_simdebug_reader->read_sample_abc(time);
+ }
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+DupliObjectData *AbcDupliCacheReader::find_dupli_data(ObjectReaderPtr ptr) const
+{
+ DupliMap::const_iterator it = dupli_map.find(ptr);
+ if (it == dupli_map.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+void AbcDupliCacheReader::insert_dupli_data(ObjectReaderPtr ptr, DupliObjectData *data)
+{
+ dupli_map.insert(DupliPair(ptr, data));
+}
+
+void AbcDupliCacheReader::build_object_map(Main *bmain, Group *group)
+{
+ BKE_main_id_tag_idcode(bmain, ID_OB, false);
+ BKE_main_id_tag_idcode(bmain, ID_GR, false);
+ object_map.clear();
+
+ build_object_map_add_group(group);
+}
+
+Object *AbcDupliCacheReader::find_object(const std::string &name) const
+{
+ ObjectMap::const_iterator it = object_map.find(name);
+ if (it == object_map.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+void AbcDupliCacheReader::build_object_map_add_group(Group *group)
+{
+ if (group->id.flag & LIB_DOIT)
+ return;
+ group->id.flag |= LIB_DOIT;
+
+ for (GroupObject *gob = (GroupObject *)group->gobject.first; gob; gob = gob->next) {
+ Object *ob = gob->ob;
+ if (ob->id.flag & LIB_DOIT)
+ continue;
+ ob->id.flag |= LIB_DOIT;
+ object_map.insert(ObjectPair(ob->id.name, ob));
+
+ if ((ob->transflag & OB_DUPLIGROUP) && ob->dup_group) {
+ build_object_map_add_group(ob->dup_group);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+AbcDupliObjectWriter::AbcDupliObjectWriter(const std::string &name, DupliObjectData *dupdata, bool do_mesh, bool do_strands) :
+ ObjectWriter(dupdata->ob, name),
+ m_dupdata(dupdata),
+ m_do_strands(do_strands),
+ m_dm_writer(0)
+{
+ if (do_mesh) {
+ if (m_ob && m_ob->type == OB_MESH) {
+ m_dm_writer = new AbcDerivedMeshWriter("mesh", dupdata->ob, &dupdata->dm);
+ }
+ }
+}
+
+AbcStrandsWriter *AbcDupliObjectWriter::find_strands_writer(const std::string &name) const
+{
+ StrandsWriters::const_iterator it = m_strands_writers.find(name);
+ return (it != m_strands_writers.end()) ? it->second : NULL;
+}
+
+AbcStrandsWriter *AbcDupliObjectWriter::add_strands_writer(const std::string &name)
+{
+ StrandsWriters::const_iterator it = m_strands_writers.find(name);
+ if (it != m_strands_writers.end()) {
+ return it->second;
+ }
+ else {
+ AbcStrandsWriter *writer = new AbcStrandsWriter(name, m_dupdata);
+ m_strands_writers.insert(StrandsWritersPair(name, writer));
+
+ writer->init(abc_archive());
+ writer->init_abc(m_abc_object);
+
+ return writer;
+ }
+}
+
+AbcDupliObjectWriter::~AbcDupliObjectWriter()
+{
+ if (m_dm_writer)
+ delete m_dm_writer;
+ for (StrandsWriters::iterator it = m_strands_writers.begin(); it != m_strands_writers.end(); ++it) {
+ if (it->second)
+ delete it->second;
+ }
+}
+
+void AbcDupliObjectWriter::init_abc()
+{
+ if (m_abc_object)
+ return;
+
+ m_abc_object = abc_archive()->add_id_object<OObject>((ID *)m_ob);
+
+ if (m_dm_writer) {
+ /* XXX not nice */
+ m_dm_writer->init(abc_archive());
+ m_dm_writer->init_abc(m_abc_object);
+ }
+}
+
+void AbcDupliObjectWriter::write_sample()
+{
+ if (!m_abc_object)
+ return;
+
+ if (m_dm_writer) {
+ m_dm_writer->write_sample();
+ }
+
+ if (m_do_strands) {
+ int index = 0;
+ for (DupliObjectDataStrands *link = (DupliObjectDataStrands *)m_dupdata->strands.first;
+ link;
+ link = link->next, ++index) {
+ AbcStrandsWriter *writer = add_strands_writer(link->name);
+ writer->write_sample();
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+AbcDupliObjectReader::AbcDupliObjectReader(const std::string &name, Object *ob, DupliObjectData *dupli_data,
+ bool read_strands_motion, bool read_strands_children) :
+ ObjectReader(ob, name),
+ dupli_data(dupli_data),
+ m_read_strands_motion(read_strands_motion),
+ m_read_strands_children(read_strands_children)
+{
+}
+
+AbcDupliObjectReader::~AbcDupliObjectReader()
+{
+}
+
+void AbcDupliObjectReader::init(ReaderArchive *archive)
+{
+ AbcReader::init(archive);
+
+ if (abc_archive()->root().getChildHeader(m_name))
+ m_abc_object = abc_archive()->root().getChild(m_name);
+}
+
+void AbcDupliObjectReader::init_abc(IObject object)
+{
+ m_abc_object = object;
+}
+
+void AbcDupliObjectReader::read_dupligroup_object(IObject object, chrono_t time)
+{
+ if (GS(object.getName().c_str()) == ID_OB) {
+ /* instances are handled later, we create true object data here */
+ if (object.isInstanceDescendant())
+ return;
+
+ BKE_dupli_object_data_init(dupli_data, m_ob);
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ IObject child = object.getChild(i);
+ const MetaData &metadata = child.getMetaData();
+
+ if (IPolyMeshSchema::matches(metadata)) {
+ AbcDerivedMeshReader dm_reader("mesh", m_ob);
+ dm_reader.init(abc_archive());
+ dm_reader.init_abc(child);
+ if (dm_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) {
+ BKE_dupli_object_data_set_mesh(dupli_data, dm_reader.acquire_result());
+ }
+ else {
+ dm_reader.discard_result();
+ }
+ }
+ else if (ICurvesSchema::matches(metadata)) {
+ Strands *strands;
+ StrandsChildren *children;
+ BKE_dupli_object_data_find_strands(dupli_data, child.getName().c_str(), &strands, &children);
+
+ AbcStrandsReader strands_reader(strands, children, m_read_strands_motion, m_read_strands_children);
+ strands_reader.init(abc_archive());
+ strands_reader.init_abc(child);
+ if (strands_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) {
+ Strands *newstrands = strands_reader.acquire_result();
+ if (strands && strands != newstrands) {
+ /* reader can replace strands internally if topology does not match */
+ BKE_strands_free(strands);
+ }
+ BKE_dupli_object_data_add_strands(dupli_data, child.getName().c_str(), newstrands);
+
+ StrandsChildren *newchildren = strands_reader.child_reader().acquire_result();
+ if (children && children != newchildren) {
+ /* reader can replace strands internally if topology does not match */
+ BKE_strands_children_free(children);
+ }
+ BKE_dupli_object_data_add_strands_children(dupli_data, child.getName().c_str(), newchildren);
+ }
+ else {
+ strands_reader.discard_result();
+ strands_reader.child_reader().discard_result();
+ }
+ }
+ }
+ }
+}
+
+PTCReadSampleResult AbcDupliObjectReader::read_sample_abc(chrono_t time)
+{
+ if (!m_abc_object)
+ return PTC_READ_SAMPLE_INVALID;
+
+ read_dupligroup_object(m_abc_object, time);
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+DupliObjectData *AbcDupliObjectReader::find_dupli_data(ObjectReaderPtr ptr) const
+{
+ DupliMap::const_iterator it = dupli_map.find(ptr);
+ if (it == dupli_map.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+void AbcDupliObjectReader::insert_dupli_data(ObjectReaderPtr ptr, DupliObjectData *data)
+{
+ dupli_map.insert(DupliPair(ptr, data));
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_group.h b/source/blender/pointcache/alembic/abc_group.h
new file mode 100644
index 00000000000..ae8c58edc45
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_group.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_GROUP_H
+#define PTC_ABC_GROUP_H
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+
+#include "util_thread.h"
+
+struct CacheLibrary;
+struct DupliCache;
+struct DupliObject;
+struct DupliObjectData;
+struct Group;
+struct Object;
+struct Scene;
+
+namespace PTC {
+
+class AbcDerivedMeshWriter;
+class AbcStrandsWriter;
+class AbcSimDebugWriter;
+class AbcSimDebugReader;
+
+class AbcGroupWriter : public GroupWriter, public AbcWriter {
+public:
+ AbcGroupWriter(const std::string &name, Group *group);
+
+ void init_abc();
+ void create_refs();
+
+ void write_sample();
+
+private:
+ Abc::OObject m_abc_object;
+};
+
+class AbcGroupReader : public GroupReader, public AbcReader {
+public:
+ AbcGroupReader(const std::string &name, Group *group);
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ Abc::IObject m_abc_object;
+};
+
+/* ========================================================================= */
+
+class AbcDupligroupWriter : public GroupWriter, public AbcWriter {
+public:
+ typedef std::vector<Abc::ObjectWriterPtr> ObjectWriterList;
+ typedef std::vector<Abc::BasePropertyWriterPtr> PropertyWriterList;
+
+ typedef std::map<ID *, AbcWriter *> IDWriterMap;
+ typedef std::pair<ID *, AbcWriter *> IDWriterPair;
+
+ AbcDupligroupWriter(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib);
+ ~AbcDupligroupWriter();
+
+ void init_abc();
+
+ void write_sample();
+ void write_sample_object(Object *ob, bool write_data);
+ void write_sample_dupli(DupliObject *dob, int index);
+
+ AbcWriter *find_id_writer(ID *id) const;
+
+private:
+ EvaluationContext *m_eval_ctx;
+ Scene *m_scene;
+ CacheLibrary *m_cachelib;
+
+ Abc::OObject m_abc_group;
+ ObjectWriterList m_object_writers;
+ PropertyWriterList m_property_writers;
+ IDWriterMap m_id_writers;
+ thread_mutex m_init_mutex;
+};
+
+class AbcDupliCacheWriter : public GroupWriter, public AbcWriter {
+public:
+ typedef std::vector<Abc::ObjectWriterPtr> ObjectWriterList;
+ typedef std::vector<Abc::BasePropertyWriterPtr> PropertyWriterList;
+
+ typedef std::map<ID *, AbcWriter *> IDWriterMap;
+ typedef std::pair<ID *, AbcWriter *> IDWriterPair;
+
+ AbcDupliCacheWriter(const std::string &name, Group *group, DupliCache *dupcache, int data_types, bool do_sim_debug = false);
+ ~AbcDupliCacheWriter();
+
+ void init_abc();
+
+ void write_sample();
+ void write_sample_object_data(DupliObjectData *data);
+ void write_sample_dupli(DupliObject *dob, int index);
+
+ AbcWriter *find_id_writer(ID *id) const;
+
+private:
+ DupliCache *m_dupcache;
+ int m_data_types;
+
+ Abc::OObject m_abc_group;
+ ObjectWriterList m_object_writers;
+ PropertyWriterList m_property_writers;
+ IDWriterMap m_id_writers;
+ AbcSimDebugWriter *m_simdebug_writer;
+};
+
+class AbcDupliCacheReader : public GroupReader, public AbcReader {
+public:
+ typedef std::map<Abc::ObjectReaderPtr, DupliObjectData*> DupliMap;
+ typedef std::pair<Abc::ObjectReaderPtr, DupliObjectData*> DupliPair;
+
+ typedef std::map<std::string, Object*> ObjectMap;
+ typedef std::pair<std::string, Object*> ObjectPair;
+
+public:
+ AbcDupliCacheReader(const std::string &name, Group *group, DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug);
+ ~AbcDupliCacheReader();
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+protected:
+ void read_dupligroup_object(Abc::IObject object, chrono_t time);
+ void read_dupligroup_group(Abc::IObject abc_group, chrono_t time);
+
+ DupliObjectData *find_dupli_data(Abc::ObjectReaderPtr ptr) const;
+ void insert_dupli_data(Abc::ObjectReaderPtr ptr, DupliObjectData *data);
+
+ void build_object_map(Main *bmain, Group *group);
+ void build_object_map_add_group(Group *group);
+ Object *find_object(const std::string &name) const;
+
+private:
+ DupliMap dupli_map;
+ DupliCache *dupli_cache;
+
+ ObjectMap object_map;
+ bool m_read_strands_motion, m_read_strands_children;
+ AbcSimDebugReader *m_simdebug_reader;
+};
+
+
+class AbcDupliObjectWriter : public ObjectWriter, public AbcWriter {
+public:
+ typedef std::map<std::string, AbcStrandsWriter *> StrandsWriters;
+ typedef std::pair<std::string, AbcStrandsWriter *> StrandsWritersPair;
+
+ AbcDupliObjectWriter(const std::string &name, DupliObjectData *dupdata, bool do_mesh, bool do_strands);
+ ~AbcDupliObjectWriter();
+
+ void init_abc();
+
+ void write_sample();
+
+ AbcStrandsWriter *find_strands_writer(const std::string &name) const;
+ AbcStrandsWriter *add_strands_writer(const std::string &name);
+
+private:
+ DupliObjectData *m_dupdata;
+ bool m_do_strands;
+
+ Abc::OObject m_abc_object;
+ AbcDerivedMeshWriter *m_dm_writer;
+ StrandsWriters m_strands_writers;
+};
+
+class AbcDupliObjectReader : public ObjectReader, public AbcReader {
+public:
+ typedef std::map<Abc::ObjectReaderPtr, DupliObjectData*> DupliMap;
+ typedef std::pair<Abc::ObjectReaderPtr, DupliObjectData*> DupliPair;
+
+public:
+ AbcDupliObjectReader(const std::string &name, Object *ob, DupliObjectData *dupli_data,
+ bool read_strands_motion, bool read_strands_children);
+ ~AbcDupliObjectReader();
+
+ void init(ReaderArchive *archive);
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+protected:
+ void read_dupligroup_object(Abc::IObject object, chrono_t time);
+
+ DupliObjectData *find_dupli_data(Abc::ObjectReaderPtr ptr) const;
+ void insert_dupli_data(Abc::ObjectReaderPtr ptr, DupliObjectData *data);
+
+private:
+ DupliMap dupli_map;
+ DupliObjectData *dupli_data;
+ bool m_read_strands_motion, m_read_strands_children;
+
+ Abc::IObject m_abc_object;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_OBJECT_H */
diff --git a/source/blender/pointcache/alembic/abc_info.cpp b/source/blender/pointcache/alembic/abc_info.cpp
new file mode 100644
index 00000000000..35d30cb6d0c
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_info.cpp
@@ -0,0 +1,516 @@
+//-*****************************************************************************
+//
+// Copyright (c) 2009-2013,
+// Sony Pictures Imageworks, Inc. and
+// Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Sony Pictures Imageworks, nor
+// Industrial Light & Magic nor the names of their contributors may be used
+// to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+//-*****************************************************************************
+
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include <Alembic/AbcGeom/All.h>
+#include <Alembic/AbcCoreAbstract/All.h>
+#include <Alembic/AbcCoreFactory/All.h>
+#include <Alembic/Util/All.h>
+#include <Alembic/Abc/TypedPropertyTraits.h>
+
+#include <sstream>
+
+#include "alembic.h"
+
+extern "C" {
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_ID.h"
+
+#include "BKE_cache_library.h"
+#include "BKE_idprop.h"
+}
+
+using namespace ::Alembic::AbcGeom;
+
+namespace PTC {
+
+static void metadata_from_idprops(MetaData &md, IDProperty *prop);
+
+void abc_metadata_from_idprops_group(MetaData &md, IDProperty *prop)
+{
+ if (!prop)
+ return;
+
+ IDProperty *child;
+ for (child = (IDProperty *)prop->data.group.first; child; child = child->next)
+ metadata_from_idprops(md, child);
+}
+
+static void metadata_from_idprops(MetaData &md, IDProperty *prop)
+{
+ /* skip default metadata entries, these are set explicitly */
+ std::string key(prop->name);
+ if (key.compare(kApplicationNameKey)==0 || key.compare(kDateWrittenKey)==0 || key.compare(kUserDescriptionKey)==0)
+ return;
+
+ switch (prop->type) {
+#if 0 /* don't support recursion yet */
+ case IDP_GROUP: {
+ metadata_from_idprops_group(md, child);
+ break;
+ }
+
+ case IDP_ARRAY: {
+ if (prop->data.pointer) {
+ IDProperty **array = (IDProperty **)prop->data.pointer;
+ for (int a = 0; a < prop->len; a++)
+ metadata_from_idprops(md, array[a]);
+ }
+ break;
+ }
+#endif
+
+ /* only string properties are used */
+ case IDP_STRING: {
+ md.set(prop->name, IDP_String(prop));
+ break;
+ }
+ }
+}
+
+void abc_metadata_to_idprops_group(const MetaData &md, IDProperty *prop)
+{
+ if (!prop)
+ return;
+
+ for (MetaData::const_iterator it = md.begin(); it != md.end(); ++it) {
+ const std::string &key = it->first;
+ const std::string &value = it->second;
+
+ /* skip default metadata entries, these are stored in CacheArchiveInfo */
+ if (key.compare(kApplicationNameKey)==0 || key.compare(kDateWrittenKey)==0 || key.compare(kUserDescriptionKey)==0)
+ continue;
+
+ IDPropertyTemplate val;
+ val.string.str = value.c_str();
+ val.string.len = value.length();
+ IDP_ReplaceInGroup(prop, IDP_New(IDP_STRING, &val, key.c_str()));
+ }
+}
+
+MetaData abc_create_archive_info(const char *app_name, const char *description, const struct tm *t, IDProperty *props)
+{
+ MetaData md;
+
+ md.set(kApplicationNameKey, app_name);
+ md.set(kUserDescriptionKey, description);
+
+ if (!t) {
+ time_t curtime = time(NULL);
+ t = localtime(&curtime);
+ }
+
+ if (t) {
+ char buf[256];
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M", t);
+ md.set(kDateWrittenKey, buf);
+ }
+
+ /* store custom properties as metadata */
+ if (props && props->type == IDP_GROUP)
+ abc_metadata_from_idprops_group(md, props);
+
+ return md;
+}
+
+/* ========================================================================= */
+
+struct stringstream {
+ stringstream(void (*cb)(void *, const char *), void *userdata) :
+ cb(cb),
+ userdata(userdata)
+ {
+ }
+
+ void (*cb)(void *, const char *);
+ void *userdata;
+
+ template <typename T>
+ friend stringstream& operator << (stringstream &stream, T s);
+};
+
+template <typename T>
+stringstream& operator << (stringstream &stream, T s)
+{
+ std::stringstream ss;
+ ss << s;
+ stream.cb(stream.userdata, ss.str().c_str());
+ return stream;
+}
+
+static const std::string g_sep(";");
+static const std::string g_endl("\n");
+
+static void info_stream_properties(stringstream &ss, ICompoundProperty, std::string &);
+
+template <class PROP>
+static void info_stream_array_property(stringstream &ss, PROP iProp, const std::string &iIndent)
+{
+ std::string ptype = "ArrayProperty ";
+ size_t asize = 0;
+
+ AbcA::ArraySamplePtr samp;
+ index_t maxSamples = iProp.getNumSamples();
+ for (index_t i = 0 ; i < maxSamples; ++i) {
+ iProp.get(samp, ISampleSelector(i));
+ asize = samp->size();
+ }
+
+ std::string mdstring = "interpretation=";
+ mdstring += iProp.getMetaData().get("interpretation");
+
+ std::stringstream dtype;
+ dtype << "datatype=";
+ dtype << iProp.getDataType();
+
+ std::stringstream asizestr;
+ asizestr << ";arraysize=";
+ asizestr << asize;
+
+ mdstring += g_sep;
+
+ mdstring += dtype.str();
+
+ mdstring += asizestr.str();
+
+ ss << iIndent << " " << ptype << "name=" << iProp.getName()
+ << g_sep << mdstring << g_sep << "numsamps="
+ << iProp.getNumSamples() << g_endl;
+}
+
+template <class PROP>
+static void info_stream_scalar_property(stringstream &ss, PROP iProp, const std::string &iIndent)
+{
+ std::string ptype = "ScalarProperty ";
+ size_t asize = 0;
+
+ const AbcA::DataType &dt = iProp.getDataType();
+ const Alembic::Util ::uint8_t extent = dt.getExtent();
+ Alembic::Util::Dimensions dims(extent);
+ AbcA::ArraySamplePtr samp = AbcA::AllocateArraySample( dt, dims );
+ index_t maxSamples = iProp.getNumSamples();
+ for (index_t i = 0 ; i < maxSamples; ++i) {
+ iProp.get(const_cast<void *>(samp->getData()), ISampleSelector(i));
+ asize = samp->size();
+ }
+
+ std::string mdstring = "interpretation=";
+ mdstring += iProp.getMetaData().get("interpretation");
+
+ std::stringstream dtype;
+ dtype << "datatype=";
+ dtype << dt;
+
+ std::stringstream asizestr;
+ asizestr << ";arraysize=";
+ asizestr << asize;
+
+ mdstring += g_sep;
+
+ mdstring += dtype.str();
+
+ mdstring += asizestr.str();
+
+ ss << iIndent << " " << ptype << "name=" << iProp.getName()
+ << g_sep << mdstring << g_sep << "numsamps="
+ << iProp.getNumSamples() << g_endl;
+}
+
+static void info_stream_compound_property(stringstream &ss, ICompoundProperty iProp, std::string &ioIndent)
+{
+ std::string oldIndent = ioIndent;
+ ioIndent += " ";
+
+ std::string interp = "schema=";
+ interp += iProp.getMetaData().get("schema");
+
+ ss << ioIndent << "CompoundProperty " << "name=" << iProp.getName()
+ << g_sep << interp << g_endl;
+
+ info_stream_properties(ss, iProp, ioIndent);
+
+ ioIndent = oldIndent;
+}
+
+static void info_stream_properties(stringstream &ss, ICompoundProperty iParent, std::string &ioIndent )
+{
+ std::string oldIndent = ioIndent;
+ for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) {
+ PropertyHeader header = iParent.getPropertyHeader(i);
+
+ if (header.isCompound()) {
+ info_stream_compound_property(ss, ICompoundProperty(iParent, header.getName()), ioIndent);
+ }
+ else if (header.isScalar()) {
+ info_stream_scalar_property(ss, IScalarProperty(iParent, header.getName()), ioIndent);
+ }
+ else {
+ BLI_assert(header.isArray());
+ info_stream_array_property(ss, IArrayProperty(iParent, header.getName()), ioIndent);
+ }
+ }
+
+ ioIndent = oldIndent;
+}
+
+static void info_stream_object(stringstream &ss, IObject iObj, std::string iIndent)
+{
+ // Object has a name, a full name, some meta data,
+ // and then it has a compound property full of properties.
+ std::string path = iObj.getFullName();
+
+ if (iObj.isInstanceRoot()) {
+ if (path != "/") {
+ ss << "Object " << "name=" << path
+ << " [Instance " << iObj.instanceSourcePath() << "]"
+ << g_endl;
+ }
+ }
+ else if (iObj.isInstanceDescendant()) {
+ /* skip non-root instances to avoid repetition */
+ return;
+ }
+ else {
+ if (path != "/") {
+ ss << "Object " << "name=" << path << g_endl;
+ }
+
+ // Get the properties.
+ ICompoundProperty props = iObj.getProperties();
+ info_stream_properties(ss, props, iIndent);
+
+ // now the child objects
+ for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) {
+ info_stream_object(ss, IObject(iObj, iObj.getChildHeader(i).getName()), iIndent);
+ }
+ }
+}
+
+void abc_archive_info_stream(IArchive &archive, void (*stream)(void *, const char *), void *userdata)
+{
+ stringstream ss(stream, userdata);
+
+ ss << "Alembic Archive Info for "
+ << Alembic::AbcCoreAbstract::GetLibraryVersion()
+ << g_endl;
+
+ std::string appName;
+ std::string libraryVersionString;
+ Alembic::Util::uint32_t libraryVersion;
+ std::string whenWritten;
+ std::string userDescription;
+ GetArchiveInfo(archive,
+ appName,
+ libraryVersionString,
+ libraryVersion,
+ whenWritten,
+ userDescription);
+
+ if (appName != "") {
+ ss << " file written by: " << appName << g_endl;
+ ss << " using Alembic : " << libraryVersionString << g_endl;
+ ss << " written on : " << whenWritten << g_endl;
+ ss << " user description : " << userDescription << g_endl;
+ ss << g_endl;
+ }
+ else {
+// ss << argv[1] << g_endl;
+ ss << " (file doesn't have any ArchiveInfo)"
+ << g_endl;
+ ss << g_endl;
+ }
+
+ info_stream_object(ss, archive.getTop(), "");
+}
+
+/* ========================================================================= */
+
+static void info_nodes_properties(CacheArchiveInfo *info, ICompoundProperty, CacheArchiveInfoNode *parent, bool calc_bytes_size);
+
+template <class PROP>
+static void info_nodes_array_property(CacheArchiveInfo *info, PROP iProp, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_ArrayProperty, iProp.getName().c_str());
+
+ index_t num_samples = iProp.getNumSamples();
+
+ const DataType &datatype = iProp.getDataType();
+
+ node->num_samples = num_samples;
+ BLI_strncpy(node->datatype_name, PODName(datatype.getPod()), sizeof(node->datatype_name));
+ node->datatype_extent = (short)datatype.getExtent();
+
+ if (calc_bytes_size) {
+ size_t max_array_size = 0;
+ size_t tot_array_size = 0;
+ for (index_t i = 0; i < num_samples; ++i) {
+ AbcA::ArraySamplePtr samp;
+ iProp.get(samp, ISampleSelector(i));
+ size_t array_size = samp->size();
+ max_array_size = std::max(max_array_size, array_size);
+ tot_array_size += array_size;
+ }
+ node->bytes_size = datatype.getNumBytes() * tot_array_size;
+ node->array_size = max_array_size;
+
+ if (parent)
+ parent->bytes_size += node->bytes_size;
+ }
+}
+
+template <class PROP>
+static void info_nodes_scalar_property(CacheArchiveInfo *info, PROP iProp, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_ScalarProperty, iProp.getName().c_str());
+
+ index_t num_samples = iProp.getNumSamples();
+
+ const DataType &datatype = iProp.getDataType();
+
+ node->num_samples = num_samples;
+ BLI_strncpy(node->datatype_name, PODName(datatype.getPod()), sizeof(node->datatype_name));
+ node->datatype_extent = (short)datatype.getExtent();
+
+ if (calc_bytes_size) {
+ node->bytes_size = datatype.getNumBytes() * num_samples;
+
+ if (parent)
+ parent->bytes_size += node->bytes_size;
+ }
+}
+
+static void info_nodes_compound_property(CacheArchiveInfo *info, ICompoundProperty iProp, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_CompoundProperty, iProp.getName().c_str());
+
+ info_nodes_properties(info, iProp, node, calc_bytes_size);
+
+ if (calc_bytes_size && parent)
+ parent->bytes_size += node->bytes_size;
+}
+
+static void info_nodes_properties(CacheArchiveInfo *info, ICompoundProperty iParent, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) {
+ PropertyHeader header = iParent.getPropertyHeader(i);
+
+ if (header.isCompound()) {
+ info_nodes_compound_property(info, ICompoundProperty(iParent, header.getName()), parent, calc_bytes_size);
+ }
+ else if (header.isScalar()) {
+ info_nodes_scalar_property(info, IScalarProperty(iParent, header.getName()), parent, calc_bytes_size);
+ }
+ else {
+ BLI_assert(header.isArray());
+ info_nodes_array_property(info, IArrayProperty(iParent, header.getName()), parent, calc_bytes_size);
+ }
+ }
+}
+
+static void info_nodes_object(CacheArchiveInfo *info, IObject iObj, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_Object, iObj.getName().c_str());
+
+ if (iObj.isInstanceRoot()) {
+ }
+ else if (iObj.isInstanceDescendant()) {
+ }
+ else {
+ // Get the properties.
+ ICompoundProperty props = iObj.getProperties();
+ info_nodes_properties(info, props, node, calc_bytes_size);
+
+ // now the child objects
+ for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) {
+ info_nodes_object(info, IObject(iObj, iObj.getChildHeader(i).getName()), node, calc_bytes_size);
+ }
+ }
+
+ if (calc_bytes_size && parent)
+ parent->bytes_size += node->bytes_size;
+}
+
+void abc_archive_info_nodes(IArchive &archive, CacheArchiveInfo *info, IDProperty *metadata, bool calc_nodes, bool calc_bytes_size)
+{
+// ss << "Alembic Archive Info for "
+// << Alembic::AbcCoreAbstract::GetLibraryVersion()
+// << g_endl;
+
+ std::string appName;
+ std::string libraryVersionString;
+ uint32_t libraryVersion;
+ std::string whenWritten;
+ std::string userDescription;
+ GetArchiveInfo(archive, appName, libraryVersionString, libraryVersion, whenWritten, userDescription);
+
+ if (appName != "") {
+ BLI_strncpy(info->app_name, appName.c_str(), sizeof(info->app_name));
+ BLI_strncpy(info->date_written, whenWritten.c_str(), sizeof(info->date_written));
+ BLI_strncpy(info->description, userDescription.c_str(), sizeof(info->description));
+ }
+ else {
+ info->app_name[0] = '\0';
+ info->date_written[0] = '\0';
+ info->description[0] = '\0';
+ }
+
+ if (metadata)
+ abc_metadata_to_idprops_group(archive.getPtr()->getMetaData(), metadata);
+
+ if (calc_nodes)
+ info_nodes_object(info, archive.getTop(), NULL, calc_bytes_size);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_interpolate.cpp b/source/blender/pointcache/alembic/abc_interpolate.cpp
new file mode 100644
index 00000000000..989734f3c56
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_interpolate.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "abc_interpolate.h"
+
+extern "C" {
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+
+
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_interpolate.h b/source/blender/pointcache/alembic/abc_interpolate.h
new file mode 100644
index 00000000000..102f4680c81
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_interpolate.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_INTERPOLATE_H
+#define PTC_ABC_INTERPOLATE_H
+
+#include <Alembic/Abc/ISampleSelector.h>
+#include <Alembic/Abc/IScalarProperty.h>
+#include <Alembic/Abc/TypedPropertyTraits.h>
+#include <Alembic/Abc/ITypedArrayProperty.h>
+#include <Alembic/Abc/ITypedScalarProperty.h>
+#include <Alembic/Abc/TypedArraySample.h>
+
+extern "C" {
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+}
+
+namespace PTC {
+
+using namespace Alembic;
+using namespace Abc;
+
+using Alembic::Util::shared_ptr;
+
+enum InterpolateSemanticDefault {
+ InterpolateSemanticDefault_None = 0,
+};
+
+enum InterpolateSemanticVector {
+ InterpolateSemanticVector_Linear = 0,
+ InterpolateSemanticVector_Slerp = 1,
+};
+
+/* linear blending for primitive types */
+template <typename T>
+BLI_INLINE T interpolate_sample(const T &val0, const T &val1, float t)
+{
+ return val0 * (1.0f-t) + val1 * t;
+}
+
+/* vector semantics */
+BLI_INLINE V3f interpolate_sample(const V3f &val0, const V3f &val1, float t, InterpolateSemanticVector semantic)
+{
+ switch (semantic) {
+ case InterpolateSemanticVector_Linear:
+ return val0 * (1.0f-t) + val1 * t;
+ case InterpolateSemanticVector_Slerp:
+ V3f result;
+ interp_v3_v3v3_slerp_safe(result.getValue(), val0.getValue(), val1.getValue(), t);
+ return result;
+ }
+ return V3f(0.0f, 0.0f, 0.0f);
+}
+
+BLI_INLINE Quatf interpolate_sample(const Quatf &val0, const Quatf &val1, float t)
+{
+ float qt0[4] = {val0.r, val0.v.x, val0.v.y, val0.v.z};
+ float qt1[4] = {val1.r, val1.v.x, val1.v.y, val1.v.z};
+ float result[4];
+ interp_qt_qtqt(result, qt0, qt1, t);
+ return Quatf(result[0], result[1], result[2], result[3]);
+}
+
+BLI_INLINE M44f interpolate_sample(const M44f &val0, const M44f &val1, float t)
+{
+ float loc[3], quat[4], size[3];
+ float loc0[3], quat0[4], size0[3];
+ float loc1[3], quat1[4], size1[3];
+ mat4_decompose(loc0, quat0, size0, (float (*)[4])val0.getValue());
+ mat4_decompose(loc1, quat1, size1, (float (*)[4])val1.getValue());
+
+ /* linear interpolation for rotation and scale */
+ interp_v3_v3v3(loc, loc0, loc1, t);
+
+ /* use simpe nlerp instead of slerp. it's faster and almost the same */
+ interp_v4_v4v4(quat, quat0, quat1, t);
+ normalize_qt(quat);
+
+ interp_v3_v3v3(size, size0, size1, t);
+
+ M44f result;
+ loc_quat_size_to_mat4((float (*)[4])result.getValue(), loc, quat, size);
+
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* These wrapper types allow calling all the interpolate_sample variants without knowing the semantics type
+ * structs are required for this, since partial specialization does not work with functions.
+ */
+
+/* forward declaration */
+template <typename ArraySampleT, typename SemanticT>
+BLI_INLINE shared_ptr<ArraySampleT> interpolate_sample(const ArraySampleT &val0, const ArraySampleT &val1, float t, SemanticT semantic);
+
+template <typename T, typename SemanticT>
+struct InterpolateSampleCaller {
+ BLI_INLINE T call(const T &val0, const T &val1, float t, SemanticT semantic)
+ {
+ return interpolate_sample(val0, val1, t, semantic);
+ }
+};
+
+template <typename T>
+struct InterpolateSampleCaller<T, InterpolateSemanticDefault> {
+ BLI_INLINE T call(const T &val0, const T &val1, float t, InterpolateSemanticDefault)
+ {
+ return interpolate_sample(val0, val1, t);
+ }
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Scalar Properties */
+
+template <typename PropT, typename SemanticT>
+typename PropT::value_type abc_interpolate_sample_linear(const PropT &prop, chrono_t time, SemanticT semantic)
+{
+ typedef typename PropT::value_type value_type;
+
+ ISampleSelector ss0(time, ISampleSelector::kFloorIndex);
+ ISampleSelector ss1(time, ISampleSelector::kCeilIndex);
+
+ index_t index0 = ss0.getIndex(prop.getTimeSampling(), prop.getNumSamples());
+ index_t index1 = ss1.getIndex(prop.getTimeSampling(), prop.getNumSamples());
+ if (index0 == index1) {
+ /* no interpolation needed */
+ return prop.getValue(ss0);
+ }
+ else {
+ chrono_t time0 = prop.getTimeSampling()->getSampleTime(index0);
+ chrono_t time1 = prop.getTimeSampling()->getSampleTime(index1);
+
+ float t = (time1 > time0) ? (time - time0) / (time1 - time0) : 0.0f;
+ return InterpolateSampleCaller<value_type, SemanticT>::call(prop.getValue(ss0), prop.getValue(ss1), t, semantic);
+ }
+}
+
+template <typename PropT>
+typename PropT::value_type abc_interpolate_sample_linear(const PropT &prop, chrono_t time)
+{
+ return abc_interpolate_sample_linear(prop, time, InterpolateSemanticDefault_None);
+}
+
+
+/* Array Properties */
+
+template <typename ArraySampleT, typename SemanticT>
+BLI_INLINE shared_ptr<ArraySampleT> interpolate_array_sample(const ArraySampleT &val0, const ArraySampleT &val1, float t, SemanticT semantic)
+{
+ typedef ArraySampleT sample_type;
+ typedef shared_ptr<ArraySampleT> sample_ptr_type;
+ typedef typename ArraySampleT::value_type value_type;
+
+ size_t size0 = val0.size();
+ size_t size1 = val1.size();
+ size_t maxsize = size0 > size1 ? size0 : size1;
+ size_t minsize = size0 < size1 ? size0 : size1;
+
+ const value_type *data0 = val0.get();
+ const value_type *data1 = val1.get();
+ value_type *result = new value_type[maxsize];
+ value_type *data = result;
+
+ for (size_t i = 0; i < minsize; ++i) {
+ *data = InterpolateSampleCaller<value_type, SemanticT>::call(*data0, *data1, t, semantic);
+ ++data;
+ ++data0;
+ ++data1;
+ }
+
+ if (size0 > minsize) {
+ for (size_t i = minsize; i < size0; ++i) {
+ *data = *data0;
+ ++data;
+ ++data0;
+ }
+ }
+ else if (size1 > minsize) {
+ for (size_t i = minsize; i < size1; ++i) {
+ *data = *data1;
+ ++data;
+ ++data1;
+ }
+ }
+
+ return sample_ptr_type(new sample_type(result, maxsize));
+}
+
+template <typename TraitsT, typename SemanticT>
+shared_ptr<typename ITypedArrayProperty<TraitsT>::sample_type> abc_interpolate_sample_linear(const ITypedArrayProperty<TraitsT> &prop, chrono_t time, SemanticT semantic)
+{
+ ISampleSelector ss0(time, ISampleSelector::kFloorIndex);
+ ISampleSelector ss1(time, ISampleSelector::kCeilIndex);
+
+ index_t index0 = ss0.getIndex(prop.getTimeSampling(), prop.getNumSamples());
+ index_t index1 = ss1.getIndex(prop.getTimeSampling(), prop.getNumSamples());
+ if (index0 == index1) {
+ /* no interpolation needed */
+ return prop.getValue(ss0);
+ }
+ else {
+ chrono_t time0 = prop.getTimeSampling()->getSampleTime(index0);
+ chrono_t time1 = prop.getTimeSampling()->getSampleTime(index1);
+
+ float t = (time1 > time0) ? (time - time0) / (time1 - time0) : 0.0f;
+ return interpolate_array_sample(*prop.getValue(ss0), *prop.getValue(ss1), t, semantic);
+ }
+}
+
+template <typename TraitsT>
+shared_ptr<typename ITypedArrayProperty<TraitsT>::sample_type> abc_interpolate_sample_linear(const ITypedArrayProperty<TraitsT> &prop, chrono_t time)
+{
+ return abc_interpolate_sample_linear(prop, time, InterpolateSemanticDefault_None);
+}
+
+} /* namespace PTC */
+
+#endif /* PTC_ABC_INTERPOLATE_H */
diff --git a/source/blender/pointcache/alembic/abc_mesh.cpp b/source/blender/pointcache/alembic/abc_mesh.cpp
new file mode 100644
index 00000000000..57f07d47099
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_mesh.cpp
@@ -0,0 +1,630 @@
+/*
+ * Copyright 2014, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "abc_interpolate.h"
+#include "abc_mesh.h"
+
+extern "C" {
+#include "BLI_math.h"
+
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_mesh.h"
+
+#include "PIL_time.h"
+}
+
+#include "PTC_api.h"
+
+//#define USE_TIMING
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+/* CD layers that are stored in generic customdata arrays created with CD_ALLOC */
+/* XXX CD_MASK_MTFACE and CD_MASK_MTEXPOLY are currently still needed as dummies for syncing
+ * particle UV and MCol layers to the mesh shader attributes ...
+ */
+static CustomDataMask CD_MASK_CACHE_EXCLUDE =
+ CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE | CD_MASK_MPOLY | CD_MASK_MLOOP |
+ /*CD_MASK_MTFACE | CD_MASK_MTEXPOLY |*/
+ CD_MASK_PROP_STR |
+ CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX |
+ CD_MASK_MDISPS | CD_MASK_CREASE | CD_MASK_BWEIGHT | CD_MASK_RECAST | CD_MASK_PAINT_MASK |
+ CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE;
+
+static CustomDataMask CD_MASK_CACHE_VERT = ~(CD_MASK_CACHE_EXCLUDE | CD_MASK_NORMAL);
+static CustomDataMask CD_MASK_CACHE_EDGE = ~(CD_MASK_CACHE_EXCLUDE);
+static CustomDataMask CD_MASK_CACHE_FACE = ~(CD_MASK_CACHE_EXCLUDE);
+static CustomDataMask CD_MASK_CACHE_POLY = ~(CD_MASK_CACHE_EXCLUDE);
+static CustomDataMask CD_MASK_CACHE_LOOP = ~(CD_MASK_CACHE_EXCLUDE);
+
+struct MVertSample {
+ std::vector<V3f> co;
+ std::vector<N3f> no;
+ std::vector<int8_t> flag;
+ std::vector<int8_t> bweight;
+};
+
+struct MEdgeSample {
+ std::vector<uint32_t> verts;
+ std::vector<int16_t> flag;
+ std::vector<int8_t> crease;
+ std::vector<int8_t> bweight;
+};
+
+struct MPolySample {
+ /*std::vector<int32_t> loopstart;*/ /* loopstart is not stored explicitly */
+ std::vector<int32_t> totloop;
+ std::vector<int16_t> mat_nr;
+ std::vector<int8_t> flag;
+};
+
+struct MLoopSample {
+ /* XXX these are unsigned int in DNA, but Alembic expects signed int */
+ std::vector<int32_t> verts;
+ std::vector<int32_t> edges;
+};
+
+AbcDerivedMeshWriter::AbcDerivedMeshWriter(const std::string &name, Object *ob, DerivedMesh **dm_ptr) :
+ DerivedMeshWriter(ob, dm_ptr, name),
+ m_vert_data_writer("vertex_data", CD_MASK_CACHE_VERT),
+ m_edge_data_writer("edge_data", CD_MASK_CACHE_EDGE),
+ m_face_data_writer("face_data", CD_MASK_CACHE_FACE),
+ m_poly_data_writer("poly_data", CD_MASK_CACHE_POLY),
+ m_loop_data_writer("loop_data", CD_MASK_CACHE_LOOP)
+{
+}
+
+AbcDerivedMeshWriter::~AbcDerivedMeshWriter()
+{
+}
+
+void AbcDerivedMeshWriter::init_abc(OObject parent)
+{
+ if (m_mesh)
+ return;
+
+ m_mesh = OPolyMesh(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OPolyMeshSchema &schema = m_mesh.getSchema();
+// OCompoundProperty geom_props = schema.getArbGeomParams();
+ OCompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_vert_normals = ON3fArrayProperty(user_props, "vertex_normals", frame_sampling());
+ m_prop_vert_flag = OCharArrayProperty(user_props, "vertex_flag", frame_sampling());
+ m_prop_vert_bweight = OCharArrayProperty(user_props, "vertex_bweight", frame_sampling());
+
+ m_prop_edge_verts = OUInt32ArrayProperty(user_props, "edge_verts", frame_sampling());
+ m_prop_edge_flag = OInt16ArrayProperty(user_props, "edge_flag", frame_sampling());
+ m_prop_edge_crease = OCharArrayProperty(user_props, "edge_crease", frame_sampling());
+ m_prop_edge_bweight = OCharArrayProperty(user_props, "edge_bweight", frame_sampling());
+
+ m_prop_poly_mat_nr = OInt16ArrayProperty(user_props, "poly_mat_nr", frame_sampling());
+ m_prop_poly_flag = OCharArrayProperty(user_props, "poly_flag", frame_sampling());
+
+ m_prop_loop_verts = OInt32ArrayProperty(user_props, "loop_verts", frame_sampling());
+ m_prop_loop_edges = OInt32ArrayProperty(user_props, "loop_edges", frame_sampling());
+
+ m_vert_data_writer.init(frame_sampling());
+ m_edge_data_writer.init(frame_sampling());
+ m_face_data_writer.init(frame_sampling());
+ m_poly_data_writer.init(frame_sampling());
+ m_loop_data_writer.init(frame_sampling());
+}
+
+/* XXX modifiers are not allowed to generate poly normals on their own!
+ * see assert in DerivedMesh.c : dm_ensure_display_normals
+ */
+#if 0
+static void ensure_normal_data(DerivedMesh *dm)
+{
+ MVert *mverts = dm->getVertArray(dm);
+ MLoop *mloops = dm->getLoopArray(dm);
+ MPoly *mpolys = dm->getPolyArray(dm);
+ CustomData *cdata = dm->getPolyDataLayout(dm);
+ float (*polynors)[3];
+ int totvert = dm->getNumVerts(dm);
+ int totloop = dm->getNumLoops(dm);
+ int totpoly = dm->getNumPolys(dm);
+
+ if (CustomData_has_layer(cdata, CD_NORMAL))
+ polynors = (float (*)[3])CustomData_get_layer(cdata, CD_NORMAL);
+ else
+ polynors = (float (*)[3])CustomData_add_layer(cdata, CD_NORMAL, CD_CALLOC, NULL, totpoly);
+
+ BKE_mesh_calc_normals_poly(mverts, totvert, mloops, mpolys, totloop, totpoly, polynors, false);
+}
+#endif
+
+static void create_sample_verts(DerivedMesh *dm, MVertSample &sample)
+{
+ MVert *mv, *mverts = dm->getVertArray(dm);
+ int i, totvert = dm->getNumVerts(dm);
+
+ sample.co.reserve(totvert);
+ sample.no.reserve(totvert);
+ sample.flag.reserve(totvert);
+ sample.bweight.reserve(totvert);
+ for (i = 0, mv = mverts; i < totvert; ++i, ++mv) {
+ float nor[3];
+
+ sample.co.push_back(V3f(mv->co[0], mv->co[1], mv->co[2]));
+
+ normal_short_to_float_v3(nor, mv->no);
+ sample.no.push_back(N3f(nor[0], nor[1], nor[2]));
+
+ sample.flag.push_back(mv->flag);
+ sample.bweight.push_back(mv->bweight);
+ }
+}
+
+static void create_sample_edges(DerivedMesh *dm, MEdgeSample &sample)
+{
+ MEdge *me, *medges = dm->getEdgeArray(dm);
+ int i, totedge = dm->getNumEdges(dm);
+
+ sample.verts.reserve(totedge * 2);
+ sample.flag.reserve(totedge);
+ sample.crease.reserve(totedge);
+ sample.bweight.reserve(totedge);
+
+ for (i = 0, me = medges; i < totedge; ++i, ++me) {
+ sample.verts.push_back(me->v1);
+ sample.verts.push_back(me->v2);
+ sample.flag.push_back(me->flag);
+ sample.crease.push_back(me->crease);
+ sample.bweight.push_back(me->bweight);
+ }
+}
+
+static void create_sample_polys(DerivedMesh *dm, MPolySample &sample)
+{
+ MPoly *mp, *mpolys = dm->getPolyArray(dm);
+ int i, totpoly = dm->getNumPolys(dm);
+
+ sample.totloop.reserve(totpoly);
+ sample.mat_nr.reserve(totpoly);
+ sample.flag.reserve(totpoly);
+
+ for (i = 0, mp = mpolys; i < totpoly; ++i, ++mp) {
+ sample.totloop.push_back(mp->totloop);
+ sample.mat_nr.push_back(mp->mat_nr);
+ sample.flag.push_back(mp->flag);
+ }
+}
+
+static void create_sample_loops(DerivedMesh *dm, MLoopSample &sample)
+{
+ MLoop *ml, *mloops = dm->getLoopArray(dm);
+ int i, totloop = dm->getNumLoops(dm);
+
+ sample.verts.reserve(totloop);
+ sample.edges.reserve(totloop);
+
+ for (i = 0, ml = mloops; i < totloop; ++i, ++ml) {
+ sample.verts.push_back(ml->v);
+ sample.edges.push_back(ml->e);
+ }
+}
+
+static N3fArraySample create_sample_loop_normals(DerivedMesh *dm, std::vector<N3f> &data)
+{
+ CustomData *cdata = dm->getLoopDataLayout(dm);
+ float (*nor)[3], (*loopnors)[3];
+ int i, totloop = dm->getNumLoops(dm);
+
+ if (!CustomData_has_layer(cdata, CD_NORMAL))
+ return N3fArraySample();
+
+ loopnors = (float (*)[3])CustomData_get_layer(cdata, CD_NORMAL);
+
+ data.reserve(totloop);
+ for (i = 0, nor = loopnors; i < totloop; ++i, ++nor) {
+ float *vec = *nor;
+ data.push_back(N3f(vec[0], vec[1], vec[2]));
+ }
+
+ return N3fArraySample(data);
+}
+
+void AbcDerivedMeshWriter::write_sample()
+{
+ if (!m_mesh)
+ return;
+
+ DerivedMesh *output_dm = *m_dm_ptr;
+ if (!output_dm)
+ return;
+
+ /* TODO make this optional by a flag? */
+ /* XXX does not work atm, see comment above */
+ /*ensure_normal_data(output_dm);*/
+
+ OPolyMeshSchema &schema = m_mesh.getSchema();
+ OCompoundProperty user_props = schema.getUserProperties();
+
+ MVertSample vert_sample;
+ MEdgeSample edge_sample;
+ MPolySample poly_sample;
+ MLoopSample loop_sample;
+
+ std::vector<N3f> loop_normals_buffer;
+
+ // TODO decide how to handle vertex/face normals, in caching vs. export ...
+ DM_ensure_normals(output_dm);
+
+ create_sample_verts(output_dm, vert_sample);
+ create_sample_edges(output_dm, edge_sample);
+ create_sample_polys(output_dm, poly_sample);
+ create_sample_loops(output_dm, loop_sample);
+
+ N3fArraySample lnormals = create_sample_loop_normals(output_dm, loop_normals_buffer);
+
+ OPolyMeshSchema::Sample sample = OPolyMeshSchema::Sample(
+ P3fArraySample(vert_sample.co),
+ Int32ArraySample(loop_sample.verts),
+ Int32ArraySample(poly_sample.totloop),
+ OV2fGeomParam::Sample(), /* XXX define how/which UV map should be considered primary for the alembic schema */
+ ON3fGeomParam::Sample(lnormals, kFacevaryingScope)
+ );
+ schema.set(sample);
+
+ m_prop_vert_normals.set(N3fArraySample(vert_sample.no));
+ m_prop_vert_flag.set(CharArraySample(vert_sample.flag));
+ m_prop_vert_bweight.set(CharArraySample(vert_sample.bweight));
+
+ m_prop_edge_verts.set(UInt32ArraySample(edge_sample.verts));
+ m_prop_edge_flag.set(Int16ArraySample(edge_sample.flag));
+ m_prop_edge_crease.set(CharArraySample(edge_sample.crease));
+ m_prop_edge_bweight.set(CharArraySample(edge_sample.bweight));
+
+ m_prop_poly_mat_nr.set(Int16ArraySample(poly_sample.mat_nr));
+ m_prop_poly_flag.set(CharArraySample(poly_sample.flag));
+
+ m_prop_loop_verts.set(Int32ArraySample(loop_sample.verts));
+ m_prop_loop_edges.set(Int32ArraySample(loop_sample.edges));
+
+ CustomData *vdata = output_dm->getVertDataLayout(output_dm);
+ int num_vdata = output_dm->getNumVerts(output_dm);
+ m_vert_data_writer.write_sample(vdata, num_vdata, user_props);
+
+ CustomData *edata = output_dm->getEdgeDataLayout(output_dm);
+ int num_edata = output_dm->getNumEdges(output_dm);
+ m_edge_data_writer.write_sample(edata, num_edata, user_props);
+
+ CustomData *pdata = output_dm->getPolyDataLayout(output_dm);
+ int num_pdata = output_dm->getNumPolys(output_dm);
+ m_poly_data_writer.write_sample(pdata, num_pdata, user_props);
+
+ CustomData *ldata = output_dm->getLoopDataLayout(output_dm);
+ int num_ldata = output_dm->getNumLoops(output_dm);
+ m_loop_data_writer.write_sample(ldata, num_ldata, user_props);
+
+ DM_ensure_tessface(output_dm);
+ CustomData *fdata = output_dm->getTessFaceDataLayout(output_dm);
+ int num_fdata = output_dm->getNumTessFaces(output_dm);
+ m_face_data_writer.write_sample(fdata, num_fdata, user_props);
+}
+
+/* ========================================================================= */
+
+AbcDerivedMeshReader::AbcDerivedMeshReader(const std::string &name, Object *ob) :
+ DerivedMeshReader(ob, name),
+ m_vert_data_reader("vertex_data", CD_MASK_CACHE_VERT),
+ m_edge_data_reader("edge_data", CD_MASK_CACHE_EDGE),
+ m_face_data_reader("face_data", CD_MASK_CACHE_FACE),
+ m_poly_data_reader("poly_data", CD_MASK_CACHE_POLY),
+ m_loop_data_reader("loop_data", CD_MASK_CACHE_LOOP)
+{
+}
+
+AbcDerivedMeshReader::~AbcDerivedMeshReader()
+{
+}
+
+void AbcDerivedMeshReader::init_abc(IObject object)
+{
+ if (m_mesh)
+ return;
+ m_mesh = IPolyMesh(object, kWrapExisting);
+
+ IPolyMeshSchema &schema = m_mesh.getSchema();
+ ICompoundProperty geom_props = schema.getArbGeomParams();
+ ICompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_vert_normals = IN3fArrayProperty(user_props, "vertex_normals", 0);
+ m_prop_vert_flag = ICharArrayProperty(user_props, "vertex_flag", 0);
+ m_prop_vert_bweight = ICharArrayProperty(user_props, "vertex_bweight", 0);
+
+ m_prop_edge_verts = IUInt32ArrayProperty(user_props, "edge_verts", 0);
+ m_prop_edge_flag = IInt16ArrayProperty(user_props, "edge_flag", 0);
+ m_prop_edge_crease = ICharArrayProperty(user_props, "edge_crease", 0);
+ m_prop_edge_bweight = ICharArrayProperty(user_props, "edge_bweight", 0);
+
+ m_prop_poly_mat_nr = IInt16ArrayProperty(user_props, "poly_mat_nr", 0);
+ m_prop_poly_flag = ICharArrayProperty(user_props, "poly_flag", 0);
+
+ m_prop_loop_verts = IInt32ArrayProperty(user_props, "loop_verts", 0);
+ m_prop_loop_edges = IInt32ArrayProperty(user_props, "loop_edges", 0);
+}
+
+static PTCReadSampleResult apply_sample_verts(DerivedMesh *dm, P3fArraySamplePtr sample_co, N3fArraySamplePtr sample_no,
+ CharArraySamplePtr sample_flag, CharArraySamplePtr sample_bweight)
+{
+ int totvert = dm->getNumVerts(dm);
+
+ if (sample_co->size() != totvert ||
+ sample_no->size() != totvert ||
+ sample_flag->size() != totvert ||
+ sample_bweight->size() != totvert)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ MVert *mv = dm->getVertArray(dm);
+ for (int i = 0; i < totvert; ++i) {
+ copy_v3_v3(mv->co, (*sample_co)[i].getValue());
+ normal_float_to_short_v3(mv->no, (*sample_no)[i].getValue());
+ mv->flag = (*sample_flag)[i];
+ mv->bweight = (*sample_bweight)[i];
+
+ ++mv;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+static PTCReadSampleResult apply_sample_edges(DerivedMesh *dm, UInt32ArraySamplePtr sample_verts, Int16ArraySamplePtr sample_flag,
+ CharArraySamplePtr sample_crease, CharArraySamplePtr sample_bweight, bool &has_edges)
+{
+ int totedge = dm->getNumEdges(dm);
+ if (sample_verts->size() != totedge * 2 ||
+ sample_flag->size() != totedge ||
+ sample_crease->size() != totedge ||
+ sample_bweight->size() != totedge)
+ {
+ has_edges = false;
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ const uint32_t *data_verts = sample_verts->get();
+ const int16_t *data_flag = sample_flag->get();
+ const int8_t *data_crease = sample_crease->get();
+ const int8_t *data_bweight = sample_bweight->get();
+
+ MEdge *me = dm->getEdgeArray(dm);
+ for (int i = 0; i < totedge; ++i) {
+ me->v1 = data_verts[0];
+ me->v2 = data_verts[1];
+ me->flag = *data_flag;
+ me->crease = *data_crease;
+ me->bweight = *data_bweight;
+
+ ++me;
+ data_verts += 2;
+ ++data_flag;
+ ++data_crease;
+ ++data_bweight;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+static PTCReadSampleResult apply_sample_polys(DerivedMesh *dm, Int32ArraySamplePtr sample_totloop, Int16ArraySamplePtr sample_mat_nr, CharArraySamplePtr sample_flag)
+{
+ int totpoly = dm->getNumPolys(dm);
+ if (sample_totloop->size() != totpoly ||
+ sample_mat_nr->size() != totpoly ||
+ sample_flag->size() != totpoly)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ const int32_t *data_totloop = sample_totloop->get();
+ const int16_t *data_mat_nr = sample_mat_nr->get();
+ const int8_t *data_flag = sample_flag->get();
+
+ int loopstart = 0;
+ MPoly *mp = dm->getPolyArray(dm);
+ for (int i = 0; i < totpoly; ++i) {
+ mp->totloop = *data_totloop;
+ mp->loopstart = loopstart;
+ mp->mat_nr = *data_mat_nr;
+ mp->flag = *data_flag;
+
+ loopstart += mp->totloop;
+
+ ++mp;
+ ++data_totloop;
+ ++data_mat_nr;
+ ++data_flag;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+static PTCReadSampleResult apply_sample_loops(DerivedMesh *dm, Int32ArraySamplePtr sample_verts, Int32ArraySamplePtr sample_edges, bool &has_edges)
+{
+ int totloop = dm->getNumLoops(dm);
+ if (sample_verts->size() != totloop)
+ return PTC_READ_SAMPLE_INVALID;
+
+ const int32_t *data_verts = sample_verts->get();
+
+ MLoop *ml = dm->getLoopArray(dm);
+ for (int i = 0; i < totloop; ++i) {
+ ml->v = *data_verts;
+
+ ++ml;
+ ++data_verts;
+ }
+
+ /* edge data is optional, if not available the edges must be recalculated */
+ if (sample_edges->size() == totloop) {
+ const int32_t *data_edges = sample_edges->get();
+
+ MLoop *ml = dm->getLoopArray(dm);
+ for (int i = 0; i < totloop; ++i) {
+ ml->e = *data_edges;
+
+ ++ml;
+ ++data_edges;
+ }
+ }
+ else {
+ has_edges = false;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+PTCReadSampleResult AbcDerivedMeshReader::read_sample_abc(chrono_t time)
+{
+#ifdef USE_TIMING
+ double start_time;
+ double time_get_sample, time_build_mesh, time_calc_edges, time_calc_normals;
+
+#define PROFILE_START \
+ start_time = PIL_check_seconds_timer();
+#define PROFILE_END(var) \
+ var = PIL_check_seconds_timer() - start_time;
+#else
+#define PROFILE_START ;
+#define PROFILE_END(var) ;
+#endif
+
+ /* discard existing result data */
+ discard_result();
+
+ if (!m_mesh)
+ return PTC_READ_SAMPLE_INVALID;
+
+ IPolyMeshSchema &schema = m_mesh.getSchema();
+ if (schema.getNumSamples() == 0)
+ return PTC_READ_SAMPLE_INVALID;
+ ICompoundProperty user_props = schema.getUserProperties();
+
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ PROFILE_START;
+ IPolyMeshSchema::Sample sample;
+ schema.get(sample, ss);
+
+ P3fArraySamplePtr vert_co = abc_interpolate_sample_linear(schema.getPositionsProperty(), time);
+ Int32ArraySamplePtr loop_verts = sample.getFaceIndices();
+ Int32ArraySamplePtr poly_totloop = sample.getFaceCounts();
+
+ N3fArraySamplePtr vnormals;
+ bool has_normals = false;
+ if (m_prop_vert_normals && m_prop_vert_normals.getNumSamples() > 0) {
+ vnormals = abc_interpolate_sample_linear(m_prop_vert_normals, time, InterpolateSemanticVector_Slerp);
+ has_normals = vnormals->valid();
+ }
+
+ UInt32ArraySamplePtr edge_verts = m_prop_edge_verts.getValue(ss);
+ Int32ArraySamplePtr loop_edges = m_prop_loop_edges.getValue(ss);
+ PROFILE_END(time_get_sample);
+
+ PROFILE_START;
+ bool has_edges = true; // XXX do we have to check for existing sample in advance?
+ int totverts = vert_co->size();
+ int totloops = loop_verts->size();
+ int totpolys = poly_totloop->size();
+ int totedges = has_edges ? edge_verts->size() >> 1 : 0;
+ m_result = CDDM_new(totverts, totedges, 0, totloops, totpolys);
+
+ apply_sample_verts(m_result, vert_co, vnormals, m_prop_vert_flag.getValue(ss), m_prop_vert_bweight.getValue(ss));
+ apply_sample_edges(m_result, edge_verts, m_prop_edge_flag.getValue(ss), m_prop_edge_crease.getValue(ss), m_prop_edge_bweight.getValue(ss), has_edges);
+ apply_sample_polys(m_result, poly_totloop, m_prop_poly_mat_nr.getValue(ss), m_prop_poly_flag.getValue(ss));
+ apply_sample_loops(m_result, loop_verts, loop_edges, has_edges);
+ PROFILE_END(time_build_mesh);
+
+ CustomData *vdata = m_result->getVertDataLayout(m_result);
+ int num_vdata = totverts;
+ m_vert_data_reader.read_sample(ss, vdata, num_vdata, user_props);
+
+ CustomData *edata = m_result->getEdgeDataLayout(m_result);
+ int num_edata = totedges;
+ m_edge_data_reader.read_sample(ss, edata, num_edata, user_props);
+
+ CustomData *pdata = m_result->getPolyDataLayout(m_result);
+ int num_pdata = totpolys;
+ m_poly_data_reader.read_sample(ss, pdata, num_pdata, user_props);
+
+ CustomData *ldata = m_result->getLoopDataLayout(m_result);
+ int num_ldata = totloops;
+ m_loop_data_reader.read_sample(ss, ldata, num_ldata, user_props);
+
+ DM_ensure_tessface(m_result);
+ CustomData *fdata = m_result->getTessFaceDataLayout(m_result);
+ int num_fdata = m_result->getNumTessFaces(m_result);
+ m_face_data_reader.read_sample(ss, fdata, num_fdata, user_props);
+
+ PROFILE_START;
+ if (!has_edges)
+ CDDM_calc_edges(m_result);
+ PROFILE_END(time_calc_edges);
+
+ PROFILE_START;
+ /* we need all normal properties defined, otherwise have to recalculate */
+ has_normals &= CustomData_has_layer(pdata, CD_NORMAL);
+ if (!has_normals) {
+ /* make sure normals are recalculated if there is no sample data */
+ m_result->dirty = (DMDirtyFlag)((int)m_result->dirty | DM_DIRTY_NORMALS);
+ }
+ DM_ensure_normals(m_result); /* only recalculates normals if no valid samples were found (has_normals == false) */
+ PROFILE_END(time_calc_normals);
+
+// BLI_assert(DM_is_valid(m_result));
+
+#ifdef USE_TIMING
+ printf("-------- Point Cache Timing --------\n");
+ printf("read sample: %f seconds\n", time_get_sample);
+ printf("build mesh: %f seconds\n", time_build_mesh);
+ printf("calculate edges: %f seconds\n", time_calc_edges);
+ printf("calculate normals: %f seconds\n", time_calc_normals);
+ printf("------------------------------------\n");
+#endif
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+/* ========================================================================= */
+
+AbcDerivedFinalRealtimeWriter::AbcDerivedFinalRealtimeWriter(const std::string &name, Object *ob) :
+ AbcDerivedMeshWriter(name, ob, &ob->derivedFinal)
+{
+}
+
+
+AbcDerivedFinalRenderWriter::AbcDerivedFinalRenderWriter(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr) :
+ AbcDerivedMeshWriter(name, ob, render_dm_ptr),
+ m_scene(scene)
+{
+}
+
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_mesh.h b/source/blender/pointcache/alembic/abc_mesh.h
new file mode 100644
index 00000000000..e00ccc3eef1
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_mesh.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2014, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_MESH_H
+#define PTC_ABC_MESH_H
+
+#include <Alembic/AbcGeom/IPolyMesh.h>
+#include <Alembic/AbcGeom/OPolyMesh.h>
+
+#include "ptc_types.h"
+
+#include "abc_customdata.h"
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+
+extern "C" {
+#include "DNA_modifier_types.h"
+
+#include "BKE_DerivedMesh.h"
+}
+
+struct Object;
+struct DerivedMesh;
+
+namespace PTC {
+
+class AbcDerivedMeshWriter : public DerivedMeshWriter, public AbcWriter {
+public:
+ AbcDerivedMeshWriter(const std::string &name, Object *ob, DerivedMesh **dm_ptr);
+ ~AbcDerivedMeshWriter();
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ AbcGeom::OPolyMesh m_mesh;
+
+ /* MVert attributes */
+ AbcGeom::ON3fArrayProperty m_prop_vert_normals;
+ AbcGeom::OCharArrayProperty m_prop_vert_flag;
+ AbcGeom::OCharArrayProperty m_prop_vert_bweight;
+
+ /* MEdge attributes */
+ AbcGeom::OUInt32ArrayProperty m_prop_edge_verts;
+ AbcGeom::OInt16ArrayProperty m_prop_edge_flag;
+ AbcGeom::OCharArrayProperty m_prop_edge_crease;
+ AbcGeom::OCharArrayProperty m_prop_edge_bweight;
+
+ /* MPoly attributes */
+ AbcGeom::OInt16ArrayProperty m_prop_poly_mat_nr;
+ AbcGeom::OCharArrayProperty m_prop_poly_flag;
+
+ /* MLoop attributes */
+ AbcGeom::OInt32ArrayProperty m_prop_loop_verts;
+ AbcGeom::OInt32ArrayProperty m_prop_loop_edges;
+
+ CustomDataWriter m_vert_data_writer;
+ CustomDataWriter m_edge_data_writer;
+ CustomDataWriter m_face_data_writer;
+ CustomDataWriter m_poly_data_writer;
+ CustomDataWriter m_loop_data_writer;
+};
+
+class AbcDerivedMeshReader : public DerivedMeshReader, public AbcReader {
+public:
+ AbcDerivedMeshReader(const std::string &name, Object *ob);
+ ~AbcDerivedMeshReader();
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ AbcGeom::IPolyMesh m_mesh;
+
+ /* MVert attributes */
+ AbcGeom::IN3fArrayProperty m_prop_vert_normals;
+ AbcGeom::ICharArrayProperty m_prop_vert_flag;
+ AbcGeom::ICharArrayProperty m_prop_vert_bweight;
+
+ /* MEdge attributes */
+ AbcGeom::IUInt32ArrayProperty m_prop_edge_verts;
+ AbcGeom::IInt16ArrayProperty m_prop_edge_flag;
+ AbcGeom::ICharArrayProperty m_prop_edge_crease;
+ AbcGeom::ICharArrayProperty m_prop_edge_bweight;
+
+ /* MPoly attributes */
+ AbcGeom::IInt16ArrayProperty m_prop_poly_mat_nr;
+ AbcGeom::ICharArrayProperty m_prop_poly_flag;
+
+ /* MLoop attributes */
+ AbcGeom::IInt32ArrayProperty m_prop_loop_verts;
+ AbcGeom::IInt32ArrayProperty m_prop_loop_edges;
+
+ CustomDataReader m_vert_data_reader;
+ CustomDataReader m_edge_data_reader;
+ CustomDataReader m_face_data_reader;
+ CustomDataReader m_poly_data_reader;
+ CustomDataReader m_loop_data_reader;
+};
+
+
+/* -------------------------------------------------------------------------
+ * Writing derived mesh results requires different variants
+ * depending on viewport/render output and whether a cache modifier is used.
+ *
+ * Render DMs are constructed on-the-fly for each sample write, since they
+ * are not constructed immediately during scene frame updates. The writer is
+ * expected to only be called once per frame and object.
+ *
+ * If a cache modifier is used it must be have be activate at the time when
+ * the DM is built. For viewport output this means it should activate the
+ * modifier during it's whole lifetime, so that it caches meshes during the
+ * scene frame update. For render output the modifier should only be active
+ * during the render DM construction.
+ * ------------------------------------------------------------------------- */
+
+
+class AbcDerivedFinalRealtimeWriter : public AbcDerivedMeshWriter {
+public:
+ AbcDerivedFinalRealtimeWriter(const std::string &name, Object *ob);
+};
+
+
+class AbcDerivedFinalRenderWriter : public AbcDerivedMeshWriter {
+public:
+ AbcDerivedFinalRenderWriter(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr);
+
+private:
+ Scene *m_scene;
+};
+
+
+} /* namespace PTC */
+
+#endif /* PTC_MESH_H */
diff --git a/source/blender/pointcache/alembic/abc_object.cpp b/source/blender/pointcache/alembic/abc_object.cpp
new file mode 100644
index 00000000000..6d4fcdb5b1e
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_object.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "abc_mesh.h"
+#include "abc_object.h"
+#include "abc_particles.h"
+
+extern "C" {
+#include "BLI_math.h"
+
+#include "DNA_object_types.h"
+
+#include "BKE_object.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+thread_mutex AbcObjectWriter::m_sample_write_mutex;
+
+AbcObjectWriter::AbcObjectWriter(const std::string &name, Scene *scene, Object *ob, bool do_mesh, bool do_hair) :
+ ObjectWriter(ob, name),
+ m_scene(scene),
+ m_final_dm(NULL),
+ m_dm_writer(0)
+{
+ if (do_mesh) {
+ if (m_ob->type == OB_MESH) {
+ m_dm_writer = new AbcDerivedMeshWriter("mesh", ob, &m_final_dm);
+ }
+ }
+
+ if (do_hair) {
+ for (ParticleSystem *psys = (ParticleSystem *)ob->particlesystem.first; psys; psys = psys->next) {
+ if (psys->part && psys->part->type == PART_HAIR) {
+ m_hair_writers.push_back(new AbcHairWriter(psys->name, ob, psys));
+ }
+ }
+ }
+}
+
+AbcObjectWriter::~AbcObjectWriter()
+{
+ if (m_dm_writer)
+ delete m_dm_writer;
+ for (int i = 0; i < m_hair_writers.size(); ++i)
+ if (m_hair_writers[i])
+ delete m_hair_writers[i];
+}
+
+void AbcObjectWriter::init_abc()
+{
+ if (m_abc_object)
+ return;
+
+ m_abc_object = abc_archive()->add_id_object<OObject>((ID *)m_ob);
+
+ if (m_dm_writer) {
+ /* XXX not nice */
+ m_dm_writer->init(abc_archive());
+ m_dm_writer->init_abc(m_abc_object);
+ }
+
+ for (int i = 0; i < m_hair_writers.size(); ++i) {
+ AbcHairWriter *hair_writer = m_hair_writers[i];
+ if (hair_writer) {
+ hair_writer->init(abc_archive());
+ hair_writer->init_abc(m_abc_object);
+ }
+ }
+}
+
+#if 0
+void AbcObjectWriter::create_refs()
+{
+ if ((m_ob->transflag & OB_DUPLIGROUP) && m_ob->dup_group) {
+ OObject abc_group = abc_archive()->get_id_object((ID *)m_ob->dup_group);
+ if (abc_group)
+ m_abc_object.addChildInstance(abc_group, "dup_group");
+ }
+}
+#endif
+
+void AbcObjectWriter::write_sample()
+{
+ if (!m_abc_object)
+ return;
+
+ if (m_dm_writer) {
+ if (abc_archive()->use_render()) {
+ m_final_dm = mesh_create_derived_render(m_scene, m_ob, CD_MASK_BAREMESH);
+
+ if (m_final_dm) {
+ {
+ thread_scoped_lock lock(m_sample_write_mutex);
+ m_dm_writer->write_sample();
+ }
+
+ m_final_dm->release(m_final_dm);
+ }
+ }
+ else {
+ m_final_dm = m_ob->derivedFinal;
+ if (!m_final_dm)
+ m_final_dm = mesh_get_derived_final(m_scene, m_ob, CD_MASK_BAREMESH);
+
+ if (m_final_dm) {
+ thread_scoped_lock lock(m_sample_write_mutex);
+ m_dm_writer->write_sample();
+ }
+ }
+ }
+
+ for (int i = 0; i < m_hair_writers.size(); ++i) {
+ AbcHairWriter *hair_writer = m_hair_writers[i];
+ if (hair_writer) {
+ thread_scoped_lock lock(m_sample_write_mutex);
+ hair_writer->write_sample();
+ }
+ }
+}
+
+
+AbcObjectReader::AbcObjectReader(const std::string &name, Object *ob) :
+ ObjectReader(ob, name)
+{
+}
+
+void AbcObjectReader::init_abc(IObject object)
+{
+ if (m_abc_object)
+ return;
+ m_abc_object = object;
+}
+
+PTCReadSampleResult AbcObjectReader::read_sample_abc(chrono_t /*time*/)
+{
+ if (!m_abc_object)
+ return PTC_READ_SAMPLE_INVALID;
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_object.h b/source/blender/pointcache/alembic/abc_object.h
new file mode 100644
index 00000000000..973a3312d1b
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_object.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_OBJECT_H
+#define PTC_ABC_OBJECT_H
+
+#include <vector>
+
+#include <Alembic/Abc/IObject.h>
+#include <Alembic/Abc/OObject.h>
+#include <Alembic/AbcGeom/IXform.h>
+#include <Alembic/AbcGeom/OXform.h>
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+
+#include "util_thread.h"
+
+struct DerivedMesh;
+struct Object;
+struct Scene;
+
+namespace PTC {
+
+class AbcDerivedMeshWriter;
+class AbcHairWriter;
+
+class AbcObjectWriter : public ObjectWriter, public AbcWriter {
+public:
+ typedef std::vector<AbcHairWriter *> HairWriters;
+
+ AbcObjectWriter(const std::string &name, Scene *scene, Object *ob, bool do_mesh, bool do_hair);
+ ~AbcObjectWriter();
+
+ void init_abc();
+#if 0
+ void create_refs();
+#endif
+
+ void write_sample();
+
+private:
+ Scene *m_scene;
+ DerivedMesh *m_final_dm;
+
+ Abc::OObject m_abc_object;
+ AbcDerivedMeshWriter *m_dm_writer;
+ HairWriters m_hair_writers;
+ static thread_mutex m_sample_write_mutex;
+};
+
+class AbcObjectReader : public ObjectReader, public AbcReader {
+public:
+ AbcObjectReader(const std::string &name, Object *ob);
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ Abc::IObject m_abc_object;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_OBJECT_H */
diff --git a/source/blender/pointcache/alembic/abc_particles.cpp b/source/blender/pointcache/alembic/abc_particles.cpp
new file mode 100644
index 00000000000..10e3ee55d3a
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_particles.cpp
@@ -0,0 +1,1284 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "abc_cloth.h"
+#include "abc_interpolate.h"
+#include "abc_mesh.h"
+#include "abc_particles.h"
+
+extern "C" {
+#include "BLI_listbase.h"
+#include "BLI_math_color.h"
+#include "BLI_math.h"
+
+#include "DNA_listBase.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+
+#include "BKE_anim.h"
+#include "BKE_mesh_sample.h"
+#include "BKE_particle.h"
+#include "BKE_strands.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+
+struct StrandsChildrenSample {
+ std::vector<int32_t> numverts;
+ std::vector<Quatf> root_rotations;
+ std::vector<V3f> root_positions;
+ std::vector<float32_t> cutoff;
+
+ std::vector<V3f> positions;
+ std::vector<float32_t> times;
+ std::vector<int32_t> parents;
+ std::vector<float32_t> parent_weights;
+ std::vector<V2f> curve_uvs;
+ std::vector<C3f> curve_vcols;
+};
+
+struct StrandsSample {
+ std::vector<int32_t> numverts;
+ std::vector<Quatf> root_rotations;
+ std::vector<uint32_t> root_orig_verts;
+ std::vector<float32_t> root_orig_weights;
+ std::vector<int32_t> root_orig_poly;
+ std::vector<uint32_t> root_orig_loops;
+
+ std::vector<V3f> positions;
+ std::vector<float32_t> times;
+ std::vector<float32_t> weights;
+
+ std::vector<V3f> motion_co;
+ std::vector<V3f> motion_vel;
+};
+
+AbcHairChildrenWriter::AbcHairChildrenWriter(const std::string &name, Object *ob, ParticleSystem *psys) :
+ ParticlesWriter(ob, psys, name)
+{
+ m_psmd = psys_get_modifier(ob, psys);
+}
+
+AbcHairChildrenWriter::~AbcHairChildrenWriter()
+{
+}
+
+void AbcHairChildrenWriter::init_abc(OObject parent)
+{
+ if (m_curves)
+ return;
+
+ /* XXX non-escaped string construction here ... */
+ m_curves = OCurves(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OCurvesSchema &schema = m_curves.getSchema();
+ OCompoundProperty geom_props = schema.getArbGeomParams();
+ OCompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_root_rot = OQuatfArrayProperty(user_props, "root_rotations", abc_archive()->frame_sampling());
+ m_prop_root_positions = OV3fArrayProperty(user_props, "root_positions", abc_archive()->frame_sampling());
+ m_param_cutoff = OFloatGeomParam(geom_props, "cutoff", false, kUniformScope, 1, 0);
+ m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, 0);
+ m_prop_parents = OInt32ArrayProperty(user_props, "parents", abc_archive()->frame_sampling());
+ m_prop_parent_weights = OFloatArrayProperty(user_props, "parent_weights", abc_archive()->frame_sampling());
+ m_prop_curve_uvs = AbcGeom::OV2fArrayProperty(user_props, "curve_uvs", abc_archive()->frame_sampling());
+ m_prop_curve_vcols = AbcGeom::OC3fArrayProperty(user_props, "curve_vcols", abc_archive()->frame_sampling());
+}
+
+static int hair_children_count_totkeys(ParticleCacheKey **pathcache, int totpart)
+{
+ int p;
+ int totkeys = 0;
+
+ if (pathcache) {
+ for (p = 0; p < totpart; ++p) {
+ ParticleCacheKey *keys = pathcache[p];
+ totkeys += keys->segments + 1;
+ }
+ }
+
+ return totkeys;
+}
+
+#if 0
+static int hair_children_parent_advance(HairKey *keys, int totkeys, float time, int k)
+{
+ for (; k + 1 < totkeys; ++k) {
+ if (keys[k+1].time > time)
+ break;
+ }
+ return k;
+}
+
+static void hair_children_calc_strand(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ChildParticle *cpa, ParticleCacheKey *keys, int maxkeys, StrandsChildrenSample &sample)
+{
+ const bool between = (psys->part->childtype == PART_CHILD_FACES);
+ ParticleData *parent[4];
+ float weight[4];
+ float hairmat[4][4][4];
+ int parent_key[4] = {0,0,0,0};
+
+ int i, k;
+
+ if (between) {
+ for (i = 0; i < 4; ++i) {
+ parent[i] = &psys->particles[cpa->pa[i]];
+ weight[i] = cpa->w[i];
+ if (parent[i])
+ psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, parent[i], hairmat[i]);
+ }
+ }
+ else {
+ parent[0] = &psys->particles[cpa->parent];
+ parent[1] = parent[2] = parent[3] = NULL;
+ weight[0] = 1.0f;
+ weight[1] = weight[2] = weight[3] = 0.0f;
+ if (parent[0])
+ psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, parent[0], hairmat[0]);
+ }
+
+ int numkeys = keys->segments + 1;
+ for (k = 0; k < numkeys; ++k) {
+ ParticleCacheKey *key = &keys[k];
+ /* XXX particle time values are too messy and confusing, recalculate */
+ float time = maxkeys > 1 ? (float)k / (float)(maxkeys-1) : 0.0f;
+
+ float parent_co[3];
+ zero_v3(parent_co);
+ for (i = 0; i < 4; ++i) {
+ if (!parent[i] || weight[i] <= 0.0f)
+ continue;
+ parent_key[i] = hair_children_parent_advance(parent[i]->hair, parent[i]->totkey, time, parent_key[i]);
+
+ float key_co[3];
+ if (parent_key[i] + 1 < parent[i]->totkey) {
+ HairKey *key0 = &parent[i]->hair[parent_key[i]];
+ HairKey *key1 = &parent[i]->hair[parent_key[i] + 1];
+ float x = (key1->time > key0->time) ? (time - key0->time) / (key1->time - key0->time) : 0.0f;
+ interp_v3_v3v3(key_co, key0->co, key1->co, x);
+ }
+ else {
+ HairKey *key0 = &parent[i]->hair[parent_key[i]];
+ copy_v3_v3(key_co, key0->co);
+ }
+
+ madd_v3_v3fl(parent_co, key_co, weight[i]);
+
+ /* Hair keys are in hair root space, pathcache keys are in world space,
+ * transform both to world space to calculate the offset
+ */
+ mul_m4_v3(hairmat[i], parent_co);
+ }
+
+ /* child position is an offset from the parent */
+ float co[3];
+ sub_v3_v3v3(co, key->co, parent_co);
+
+ sample.positions.push_back(V3f(parent_co[0], parent_co[1], parent_co[2]));
+ sample.times.push_back(time);
+ }
+}
+#endif
+
+BLI_INLINE bool particle_get_face(ParticleSystem *psys, int num_tessface, ChildParticle *cpa, int *r_num, float **r_fuv)
+{
+ ParticleSettings *part = psys->part;
+ const bool between = (part->childtype == PART_CHILD_FACES);
+
+ int num = DMCACHE_NOTFOUND;
+ float *fuv;
+ if (between) {
+ num = cpa->num;
+ fuv = cpa->fuv;
+ }
+ else if (part->from == PART_FROM_FACE) {
+ ParticleData *pa = psys->particles + cpa->pa[0];
+ num = pa->num_dmcache;
+ if (num == DMCACHE_NOTFOUND)
+ num = pa->num;
+ if (num >= num_tessface) {
+ /* happens when simplify is enabled gives invalid coords but would crash otherwise */
+ num = DMCACHE_NOTFOUND;
+ }
+ fuv = pa->fuv;
+ }
+
+ if (ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
+ return false;
+ }
+ else {
+ *r_num = num;
+ *r_fuv = fuv;
+ return true;
+ }
+}
+
+static void hair_children_get_uvs(ParticleSystem *psys, ParticleSystemModifierData *psmd, int totpart, StrandsChildrenSample &sample)
+{
+ const int num_tessface = psmd->dm->getNumTessFaces(psmd->dm);
+
+ MFace *mface = (MFace *)psmd->dm->getTessFaceArray(psmd->dm);
+ const CustomData *facedata = psmd->dm->getTessFaceDataLayout(psmd->dm);
+ int tot_layers = CustomData_number_of_layers(facedata, CD_MTFACE);
+
+ sample.curve_uvs.reserve(tot_layers * totpart);
+
+ for (int num_uv = 0; num_uv < tot_layers; ++num_uv) {
+ MTFace *mtface = (MTFace *)CustomData_get_layer_n(facedata, CD_MTFACE, num_uv);
+ if (!mtface)
+ continue;
+
+ for (int p = 0; p < totpart; ++p) {
+ ChildParticle *cpa = &psys->child[p];
+
+ int num;
+ float *fuv;
+ if (particle_get_face(psys, num_tessface, cpa, &num, &fuv)) {
+ MFace *mf = mface + num;
+ MTFace *mtf = mtface + num;
+
+ float uv[2];
+ psys_interpolate_uvs(mtf, mf->v4, fuv, uv);
+ sample.curve_uvs.push_back(V2f(uv[0], uv[1]));
+ }
+ else {
+ sample.curve_uvs.push_back(V2f(0.0f, 0.0f));
+ }
+ }
+ }
+}
+
+static void hair_children_get_vcols(ParticleSystem *psys, ParticleSystemModifierData *psmd, int totpart, StrandsChildrenSample &sample)
+{
+ const int num_tessface = psmd->dm->getNumTessFaces(psmd->dm);
+
+ MFace *mface = (MFace *)psmd->dm->getTessFaceArray(psmd->dm);
+ const CustomData *facedata = psmd->dm->getTessFaceDataLayout(psmd->dm);
+ int tot_layers = CustomData_number_of_layers(facedata, CD_MCOL);
+
+ sample.curve_vcols.reserve(tot_layers * totpart);
+
+ for (int num_vcol = 0; num_vcol < tot_layers; ++num_vcol) {
+ MCol *mcol = (MCol *)CustomData_get_layer_n(facedata, CD_MCOL, num_vcol);
+ if (!mcol)
+ continue;
+
+ for (int p = 0; p < totpart; ++p) {
+ ChildParticle *cpa = &psys->child[p];
+
+ int num;
+ float *fuv;
+ if (particle_get_face(psys, num_tessface, cpa, &num, &fuv)) {
+ MFace *mf = mface + num;
+ /* XXX another legacy thing: MCol are tessface data, but 4 values per face ... */
+ MCol *mc = mcol + 4 * num;
+
+ MCol col;
+ psys_interpolate_mcol(mc, mf->v4, fuv, &col);
+ /* XXX stupid legacy code: MCol stores values are BGR */
+ unsigned char icol[3] = {col.b, col.g, col.r};
+ C3f fcol;
+ rgb_uchar_to_float(fcol.getValue(), icol);
+ sample.curve_vcols.push_back(fcol);
+ }
+ else {
+ sample.curve_vcols.push_back(C3f(0.0f, 0.0f, 0.0f));
+ }
+ }
+ }
+}
+
+static void hair_children_create_sample(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ParticleCacheKey **pathcache, int totpart, int totkeys, int maxkeys,
+ StrandsChildrenSample &sample, bool write_constants)
+{
+ ParticleSettings *part = psys->part;
+ const bool between = (part->childtype == PART_CHILD_FACES);
+
+ int p, k;
+
+ if (write_constants) {
+ sample.numverts.reserve(totpart);
+ sample.parents.reserve(4*totpart);
+ sample.parent_weights.reserve(4*totpart);
+
+ sample.positions.reserve(totkeys);
+ sample.times.reserve(totkeys);
+ }
+
+ sample.root_rotations.reserve(totpart);
+ sample.root_positions.reserve(totpart);
+
+ for (p = 0; p < totpart; ++p) {
+ ChildParticle *cpa = &psys->child[p];
+
+ float hairmat[4][4];
+ psys_child_mat_to_object(ob, psys, psmd, cpa, hairmat);
+
+ if (pathcache) {
+ ParticleCacheKey *keys = pathcache[p];
+ int numkeys = keys->segments + 1;
+
+ if (write_constants) {
+ sample.numverts.push_back(numkeys);
+ if (between) {
+ sample.parents.push_back(cpa->pa[0]);
+ sample.parents.push_back(cpa->pa[1]);
+ sample.parents.push_back(cpa->pa[2]);
+ sample.parents.push_back(cpa->pa[3]);
+ sample.parent_weights.push_back(cpa->w[0]);
+ sample.parent_weights.push_back(cpa->w[1]);
+ sample.parent_weights.push_back(cpa->w[2]);
+ sample.parent_weights.push_back(cpa->w[3]);
+ }
+ else {
+ sample.parents.push_back(cpa->parent);
+ sample.parents.push_back(-1);
+ sample.parents.push_back(-1);
+ sample.parents.push_back(-1);
+ sample.parent_weights.push_back(1.0f);
+ sample.parent_weights.push_back(0.0f);
+ sample.parent_weights.push_back(0.0f);
+ sample.parent_weights.push_back(0.0f);
+ }
+
+ float imat[4][4];
+ mul_m4_m4m4(imat, ob->obmat, hairmat);
+ invert_m4(imat);
+
+ for (k = 0; k < numkeys; ++k) {
+ ParticleCacheKey *key = &keys[k];
+
+ /* pathcache keys are in world space, transform to hair root space */
+ float co[3];
+ mul_v3_m4v3(co, imat, key->co);
+
+ sample.positions.push_back(V3f(co[0], co[1], co[2]));
+ /* XXX particle time values are too messy and confusing, recalculate */
+ sample.times.push_back(maxkeys > 1 ? (float)k / (float)(maxkeys-1) : 0.0f);
+ }
+ }
+ }
+
+ float qt[4];
+ mat4_to_quat(qt, hairmat);
+ sample.root_rotations.push_back(Quatf(qt[0], qt[1], qt[2], qt[3]));
+ float *co = hairmat[3];
+ sample.root_positions.push_back(V3f(co[0], co[1], co[2]));
+ sample.cutoff.push_back(-1.0f);
+ }
+
+ if (write_constants) {
+ DM_ensure_tessface(psmd->dm);
+ hair_children_get_uvs(psys, psmd, totpart, sample);
+ hair_children_get_vcols(psys, psmd, totpart, sample);
+ }
+}
+
+void AbcHairChildrenWriter::write_sample()
+{
+ if (!m_curves)
+ return;
+
+ int totkeys = hair_children_count_totkeys(m_psys->childcache, m_psys->totchild);
+
+ int keysteps = abc_archive()->use_render() ? m_psys->part->ren_step : m_psys->part->draw_step;
+ int maxkeys = (1 << keysteps) + 1 + (m_psys->part->kink);
+ if (ELEM(m_psys->part->kink, PART_KINK_SPIRAL))
+ maxkeys += m_psys->part->kink_extra_steps;
+
+ OCurvesSchema &schema = m_curves.getSchema();
+
+ StrandsChildrenSample child_sample;
+ OCurvesSchema::Sample sample;
+ if (schema.getNumSamples() == 0) {
+ /* write curve sizes only first time, assuming they are constant! */
+ hair_children_create_sample(m_ob, m_psys, m_psmd, m_psys->childcache, m_psys->totchild, totkeys, maxkeys, child_sample, true);
+ sample = OCurvesSchema::Sample(child_sample.positions, child_sample.numverts);
+
+ m_prop_parents.set(Int32ArraySample(child_sample.parents));
+ m_prop_parent_weights.set(FloatArraySample(child_sample.parent_weights));
+ m_prop_curve_uvs.set(V2fArraySample(child_sample.curve_uvs));
+ m_prop_curve_vcols.set(C3fArraySample(child_sample.curve_vcols));
+
+ m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(child_sample.times), kVertexScope));
+
+ schema.set(sample);
+ }
+ else {
+ hair_children_create_sample(m_ob, m_psys, m_psmd, m_psys->childcache, m_psys->totchild, totkeys, maxkeys, child_sample, false);
+ }
+
+ m_prop_root_rot.set(QuatfArraySample(child_sample.root_rotations));
+ m_prop_root_positions.set(V3fArraySample(child_sample.root_positions));
+ m_param_cutoff.set(OFloatGeomParam::Sample(FloatArraySample(child_sample.cutoff), kUniformScope));
+}
+
+
+AbcHairWriter::AbcHairWriter(const std::string &name, Object *ob, ParticleSystem *psys) :
+ ParticlesWriter(ob, psys, name),
+ m_child_writer("children", ob, psys)
+{
+ m_psmd = psys_get_modifier(ob, psys);
+}
+
+AbcHairWriter::~AbcHairWriter()
+{
+}
+
+void AbcHairWriter::init(WriterArchive *archive)
+{
+ AbcWriter::init(archive);
+ m_child_writer.init(archive);
+}
+
+void AbcHairWriter::init_abc(OObject parent)
+{
+ if (m_curves)
+ return;
+ m_curves = OCurves(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OCurvesSchema &schema = m_curves.getSchema();
+ OCompoundProperty geom_props = schema.getArbGeomParams();
+
+ m_param_root_rot = OQuatfGeomParam(geom_props, "root_rotations", false, kUniformScope, 1, 0);
+ m_param_root_orig_verts = OUInt32GeomParam(geom_props, "root_orig_verts", false, kUniformScope, 1, 0);
+ m_param_root_orig_weights = OFloatGeomParam(geom_props, "root_orig_weights", false, kUniformScope, 1, 0);
+ m_param_root_orig_poly = OInt32GeomParam(geom_props, "root_orig_poly", false, kUniformScope, 1, 0);
+ m_param_root_orig_loops = OUInt32GeomParam(geom_props, "root_orig_loops", false, kUniformScope, 1, 0);
+
+ m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, 0);
+ m_param_weights = OFloatGeomParam(geom_props, "weights", false, kVertexScope, 1, 0);
+
+ m_child_writer.init_abc(m_curves);
+}
+
+static int hair_count_totverts(ParticleSystem *psys)
+{
+ int p;
+ int totverts = 0;
+
+ for (p = 0; p < psys->totpart; ++p) {
+ ParticleData *pa = &psys->particles[p];
+ totverts += pa->totkey;
+ }
+
+ return totverts;
+}
+
+static void hair_create_sample(Object *ob, DerivedMesh *dm, ParticleSystem *psys, StrandsSample &sample, bool do_numverts)
+{
+ int totpart = psys->totpart;
+ int totverts = hair_count_totverts(psys);
+ int p, k;
+
+ if (totverts == 0)
+ return;
+
+ if (do_numverts)
+ sample.numverts.reserve(totpart);
+ sample.root_rotations.reserve(totpart);
+ sample.root_orig_verts.reserve(totpart * 3);
+ sample.root_orig_weights.reserve(totpart * 3);
+ sample.root_orig_poly.reserve(totpart);
+ sample.root_orig_loops.reserve(totpart * 3);
+ sample.positions.reserve(totverts);
+ sample.times.reserve(totverts);
+ sample.weights.reserve(totverts);
+
+ for (p = 0; p < totpart; ++p) {
+ ParticleData *pa = &psys->particles[p];
+ int numverts = pa->totkey;
+ float hairmat[4][4];
+
+ if (do_numverts)
+ sample.numverts.push_back(numverts);
+
+ psys_mat_hair_to_object(ob, dm, psys->part->from, pa, hairmat);
+ float root_qt[4];
+ mat4_to_quat(root_qt, hairmat);
+ sample.root_rotations.push_back(Quatf(root_qt[0], root_qt[1], root_qt[2], root_qt[3]));
+
+ MSurfaceSample surf;
+ BKE_mesh_sample_from_particle(&surf, psys, dm, pa);
+ sample.root_orig_verts.push_back(surf.orig_verts[0]);
+ sample.root_orig_verts.push_back(surf.orig_verts[1]);
+ sample.root_orig_verts.push_back(surf.orig_verts[2]);
+ sample.root_orig_weights.push_back(surf.orig_weights[0]);
+ sample.root_orig_weights.push_back(surf.orig_weights[1]);
+ sample.root_orig_weights.push_back(surf.orig_weights[2]);
+ sample.root_orig_poly.push_back(surf.orig_poly);
+ sample.root_orig_loops.push_back(surf.orig_loops[0]);
+ sample.root_orig_loops.push_back(surf.orig_loops[1]);
+ sample.root_orig_loops.push_back(surf.orig_loops[2]);
+
+ for (k = 0; k < numverts; ++k) {
+ HairKey *key = &pa->hair[k];
+
+ /* hair keys are in "hair space" relative to the mesh,
+ * store them in object space for compatibility and to avoid
+ * complexities of how particles work.
+ */
+ float co[3];
+ mul_v3_m4v3(co, hairmat, key->co);
+
+ sample.positions.push_back(V3f(co[0], co[1], co[2]));
+ /* XXX particle time values are too messy and confusing, recalculate */
+ sample.times.push_back(numverts > 1 ? (float)k / (float)(numverts-1) : 0.0f);
+ sample.weights.push_back(key->weight);
+ }
+ }
+}
+
+void AbcHairWriter::write_sample()
+{
+ if (!m_curves)
+ return;
+ if (!m_psmd || !m_psmd->dm)
+ return;
+
+ OCurvesSchema &schema = m_curves.getSchema();
+
+ StrandsSample hair_sample;
+ OCurvesSchema::Sample sample;
+ if (schema.getNumSamples() == 0) {
+ /* write curve sizes only first time, assuming they are constant! */
+ hair_create_sample(m_ob, m_psmd->dm, m_psys, hair_sample, true);
+ sample = OCurvesSchema::Sample(hair_sample.positions, hair_sample.numverts);
+ }
+ else {
+ hair_create_sample(m_ob, m_psmd->dm, m_psys, hair_sample, false);
+ sample = OCurvesSchema::Sample(hair_sample.positions);
+ }
+ schema.set(sample);
+
+ m_param_root_rot.set(OQuatfGeomParam::Sample(QuatfArraySample(hair_sample.root_rotations), kUniformScope));
+ m_param_root_orig_verts.set(OUInt32GeomParam::Sample(UInt32ArraySample(hair_sample.root_orig_verts), kUniformScope));
+ m_param_root_orig_weights.set(OFloatGeomParam::Sample(FloatArraySample(hair_sample.root_orig_weights), kUniformScope));
+ m_param_root_orig_poly.set(OInt32GeomParam::Sample(Int32ArraySample(hair_sample.root_orig_poly), kUniformScope));
+ m_param_root_orig_loops.set(OUInt32GeomParam::Sample(UInt32ArraySample(hair_sample.root_orig_loops), kUniformScope));
+
+ m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(hair_sample.times), kVertexScope));
+ m_param_weights.set(OFloatGeomParam::Sample(FloatArraySample(hair_sample.weights), kVertexScope));
+
+ m_child_writer.write_sample();
+}
+
+
+AbcStrandsChildrenWriter::AbcStrandsChildrenWriter(const std::string &name, const std::string &abc_name, DupliObjectData *dobdata) :
+ m_name(name),
+ m_abc_name(abc_name),
+ m_dobdata(dobdata)
+{
+}
+
+StrandsChildren *AbcStrandsChildrenWriter::get_strands() const
+{
+ StrandsChildren *children;
+ BKE_dupli_object_data_find_strands(m_dobdata, m_name.c_str(), NULL, &children);
+ return children;
+}
+
+void AbcStrandsChildrenWriter::init_abc(OObject parent)
+{
+ if (m_curves)
+ return;
+ m_curves = OCurves(parent, m_abc_name, abc_archive()->frame_sampling_index());
+
+ OCurvesSchema &schema = m_curves.getSchema();
+ OCompoundProperty geom_props = schema.getArbGeomParams();
+ OCompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_root_rot = OQuatfArrayProperty(user_props, "root_rotations", abc_archive()->frame_sampling());
+ m_prop_root_positions = OV3fArrayProperty(user_props, "root_positions", abc_archive()->frame_sampling());
+ m_param_cutoff = OFloatGeomParam(geom_props, "cutoff", false, kUniformScope, 1, abc_archive()->frame_sampling());
+ m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, abc_archive()->frame_sampling());
+ m_prop_parents = OInt32ArrayProperty(user_props, "parents", abc_archive()->frame_sampling());
+ m_prop_parent_weights = OFloatArrayProperty(user_props, "parent_weights", abc_archive()->frame_sampling());
+ m_prop_curve_uvs = AbcGeom::OV2fArrayProperty(user_props, "curve_uvs", abc_archive()->frame_sampling());
+ m_prop_curve_vcols = AbcGeom::OC3fArrayProperty(user_props, "curve_vcols", abc_archive()->frame_sampling());
+}
+
+static void strands_children_get_uvs(StrandsChildren *strands, StrandsChildrenSample &sample)
+{
+ int totuv = strands->numuv * strands->totcurves;
+
+ sample.curve_uvs.reserve(totuv);
+
+ for (int i = 0; i < totuv; ++i) {
+ StrandsChildCurveUV *uv = &strands->curve_uvs[i];
+ sample.curve_uvs.push_back(V2f(uv->uv[0], uv->uv[1]));
+ }
+}
+
+static void strands_children_get_vcols(StrandsChildren *strands, StrandsChildrenSample &sample)
+{
+ int totvcol = strands->numvcol * strands->totcurves;
+
+ sample.curve_vcols.reserve(totvcol);
+
+ for (int i = 0; i < totvcol; ++i) {
+ StrandsChildCurveVCol *vcol = &strands->curve_vcols[i];
+ sample.curve_vcols.push_back(C3f(vcol->vcol[0], vcol->vcol[1], vcol->vcol[2]));
+ }
+}
+
+static void strands_children_create_sample(StrandsChildren *strands, StrandsChildrenSample &sample, bool write_constants)
+{
+ int totcurves = strands->totcurves;
+ int totverts = strands->totverts;
+
+ if (write_constants) {
+ sample.numverts.reserve(totcurves);
+ sample.parents.reserve(4*totcurves);
+ sample.parent_weights.reserve(4*totcurves);
+
+ sample.positions.reserve(totverts);
+ sample.times.reserve(totverts);
+ }
+
+ sample.root_rotations.reserve(totcurves);
+ sample.root_positions.reserve(totcurves);
+
+ StrandChildIterator it_strand;
+ for (BKE_strand_child_iter_init(&it_strand, strands); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) {
+ int numverts = it_strand.curve->numverts;
+
+ if (write_constants) {
+ sample.numverts.push_back(numverts);
+
+ sample.parents.push_back(it_strand.curve->parents[0]);
+ sample.parents.push_back(it_strand.curve->parents[1]);
+ sample.parents.push_back(it_strand.curve->parents[2]);
+ sample.parents.push_back(it_strand.curve->parents[3]);
+ sample.parent_weights.push_back(it_strand.curve->parent_weights[0]);
+ sample.parent_weights.push_back(it_strand.curve->parent_weights[1]);
+ sample.parent_weights.push_back(it_strand.curve->parent_weights[2]);
+ sample.parent_weights.push_back(it_strand.curve->parent_weights[3]);
+
+ StrandChildVertexIterator it_vert;
+ for (BKE_strand_child_vertex_iter_init(&it_vert, &it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) {
+ const float *co = it_vert.vertex->base;
+ sample.positions.push_back(V3f(co[0], co[1], co[2]));
+ sample.times.push_back(it_vert.vertex->time);
+ }
+ }
+
+ float qt[4];
+ mat4_to_quat(qt, it_strand.curve->root_matrix);
+ sample.root_rotations.push_back(Quatf(qt[0], qt[1], qt[2], qt[3]));
+ float *co = it_strand.curve->root_matrix[3];
+ sample.root_positions.push_back(V3f(co[0], co[1], co[2]));
+ sample.cutoff.push_back(it_strand.curve->cutoff);
+ }
+
+ if (write_constants) {
+ strands_children_get_uvs(strands, sample);
+ strands_children_get_vcols(strands, sample);
+ }
+}
+
+void AbcStrandsChildrenWriter::write_sample()
+{
+ if (!m_curves)
+ return;
+ StrandsChildren *strands = get_strands();
+ if (!strands)
+ return;
+
+ OCurvesSchema &schema = m_curves.getSchema();
+
+ StrandsChildrenSample strands_sample;
+ OCurvesSchema::Sample sample;
+ if (schema.getNumSamples() == 0) {
+ /* write curve sizes only first time, assuming they are constant! */
+ strands_children_create_sample(strands, strands_sample, true);
+ sample = OCurvesSchema::Sample(strands_sample.positions, strands_sample.numverts);
+
+ m_prop_parents.set(Int32ArraySample(strands_sample.parents));
+ m_prop_parent_weights.set(FloatArraySample(strands_sample.parent_weights));
+ m_prop_curve_uvs.set(V2fArraySample(strands_sample.curve_uvs));
+ m_prop_curve_vcols.set(C3fArraySample(strands_sample.curve_vcols));
+
+ m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.times), kVertexScope));
+
+ schema.set(sample);
+ }
+ else {
+ strands_children_create_sample(strands, strands_sample, false);
+ }
+
+ m_prop_root_rot.set(QuatfArraySample(strands_sample.root_rotations));
+ m_prop_root_positions.set(V3fArraySample(strands_sample.root_positions));
+ m_param_cutoff.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.cutoff), kUniformScope));
+}
+
+
+AbcStrandsWriter::AbcStrandsWriter(const std::string &name, DupliObjectData *dobdata) :
+ m_name(name),
+ m_dobdata(dobdata),
+ m_child_writer(name, "children", dobdata)
+{
+}
+
+Strands *AbcStrandsWriter::get_strands() const
+{
+ Strands *strands;
+ BKE_dupli_object_data_find_strands(m_dobdata, m_name.c_str(), &strands, NULL);
+ return strands;
+}
+
+void AbcStrandsWriter::init(WriterArchive *archive)
+{
+ AbcWriter::init(archive);
+ m_child_writer.init(archive);
+}
+
+void AbcStrandsWriter::init_abc(OObject parent)
+{
+ if (m_curves)
+ return;
+ m_curves = OCurves(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OCurvesSchema &schema = m_curves.getSchema();
+ OCompoundProperty geom_props = schema.getArbGeomParams();
+
+ m_param_root_rot = OQuatfGeomParam(geom_props, "root_rotations", false, kUniformScope, 1, abc_archive()->frame_sampling());
+ m_param_root_orig_verts = OUInt32GeomParam(geom_props, "root_orig_verts", false, kUniformScope, 1, 0);
+ m_param_root_orig_weights = OFloatGeomParam(geom_props, "root_orig_weights", false, kUniformScope, 1, 0);
+ m_param_root_orig_poly = OInt32GeomParam(geom_props, "root_orig_poly", false, kUniformScope, 1, 0);
+ m_param_root_orig_loops = OUInt32GeomParam(geom_props, "root_orig_loops", false, kUniformScope, 1, 0);
+
+ m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, abc_archive()->frame_sampling());
+ m_param_weights = OFloatGeomParam(geom_props, "weights", false, kVertexScope, 1, abc_archive()->frame_sampling());
+
+ m_param_motion_state = OCompoundProperty(geom_props, "motion_state", abc_archive()->frame_sampling());
+ m_param_motion_co = OP3fGeomParam(m_param_motion_state, "position", false, kVertexScope, 1, abc_archive()->frame_sampling());
+ m_param_motion_vel = OV3fGeomParam(m_param_motion_state, "velocity", false, kVertexScope, 1, abc_archive()->frame_sampling());
+
+ m_child_writer.init_abc(m_curves);
+}
+
+static void strands_create_sample(Strands *strands, StrandsSample &sample, bool do_numverts)
+{
+ const bool do_state = strands->state;
+
+ int totcurves = strands->totcurves;
+ int totverts = strands->totverts;
+
+ if (totverts == 0)
+ return;
+
+ if (do_numverts)
+ sample.numverts.reserve(totcurves);
+ sample.root_rotations.reserve(totcurves);
+ sample.root_orig_verts.reserve(totcurves * 3);
+ sample.root_orig_weights.reserve(totcurves * 3);
+ sample.root_orig_poly.reserve(totcurves);
+ sample.root_orig_loops.reserve(totcurves * 3);
+
+ sample.positions.reserve(totverts);
+ sample.times.reserve(totverts);
+ sample.weights.reserve(totverts);
+ if (do_state) {
+ sample.motion_co.reserve(totverts);
+ sample.motion_vel.reserve(totverts);
+ }
+
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ int numverts = it_strand.curve->numverts;
+
+ if (do_numverts)
+ sample.numverts.push_back(numverts);
+ float qt[4];
+ mat3_to_quat(qt, it_strand.curve->root_matrix);
+ sample.root_rotations.push_back(Quatf(qt[0], qt[1], qt[2], qt[3]));
+
+ sample.root_orig_verts.push_back(it_strand.curve->msurf.orig_verts[0]);
+ sample.root_orig_verts.push_back(it_strand.curve->msurf.orig_verts[1]);
+ sample.root_orig_verts.push_back(it_strand.curve->msurf.orig_verts[2]);
+ sample.root_orig_weights.push_back(it_strand.curve->msurf.orig_weights[0]);
+ sample.root_orig_weights.push_back(it_strand.curve->msurf.orig_weights[1]);
+ sample.root_orig_weights.push_back(it_strand.curve->msurf.orig_weights[2]);
+ sample.root_orig_poly.push_back(it_strand.curve->msurf.orig_poly);
+ sample.root_orig_loops.push_back(it_strand.curve->msurf.orig_loops[0]);
+ sample.root_orig_loops.push_back(it_strand.curve->msurf.orig_loops[1]);
+ sample.root_orig_loops.push_back(it_strand.curve->msurf.orig_loops[2]);
+
+ StrandVertexIterator it_vert;
+ for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+ const float *co = it_vert.vertex->co;
+ sample.positions.push_back(V3f(co[0], co[1], co[2]));
+ sample.times.push_back(it_vert.vertex->time);
+ sample.weights.push_back(it_vert.vertex->weight);
+
+ if (do_state) {
+ float *co = it_vert.state->co;
+ float *vel = it_vert.state->vel;
+ sample.motion_co.push_back(V3f(co[0], co[1], co[2]));
+ sample.motion_vel.push_back(V3f(vel[0], vel[1], vel[2]));
+ }
+ }
+ }
+}
+
+void AbcStrandsWriter::write_sample()
+{
+ if (!m_curves)
+ return;
+ Strands *strands = get_strands();
+ if (!strands)
+ return;
+
+ OCurvesSchema &schema = m_curves.getSchema();
+
+ StrandsSample strands_sample;
+ OCurvesSchema::Sample sample;
+ if (schema.getNumSamples() == 0) {
+ /* write curve sizes only first time, assuming they are constant! */
+ strands_create_sample(strands, strands_sample, true);
+ sample = OCurvesSchema::Sample(strands_sample.positions, strands_sample.numverts);
+ }
+ else {
+ strands_create_sample(strands, strands_sample, false);
+ sample = OCurvesSchema::Sample(strands_sample.positions);
+ }
+ schema.set(sample);
+
+ m_param_root_rot.set(OQuatfGeomParam::Sample(QuatfArraySample(strands_sample.root_rotations), kUniformScope));
+ m_param_root_orig_verts.set(OUInt32GeomParam::Sample(UInt32ArraySample(strands_sample.root_orig_verts), kUniformScope));
+ m_param_root_orig_weights.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.root_orig_weights), kUniformScope));
+ m_param_root_orig_poly.set(OInt32GeomParam::Sample(Int32ArraySample(strands_sample.root_orig_poly), kUniformScope));
+ m_param_root_orig_loops.set(OUInt32GeomParam::Sample(UInt32ArraySample(strands_sample.root_orig_loops), kUniformScope));
+
+ m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.times), kVertexScope));
+ m_param_weights.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.weights), kVertexScope));
+
+ if (strands->state) {
+ m_param_motion_co.set(OP3fGeomParam::Sample(P3fArraySample(strands_sample.motion_co), kVertexScope));
+ m_param_motion_vel.set(OV3fGeomParam::Sample(V3fArraySample(strands_sample.motion_vel), kVertexScope));
+ }
+
+ m_child_writer.write_sample();
+}
+
+#if 0
+#define PRINT_M3_FORMAT "((%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f))"
+#define PRINT_M3_ARGS(m) (double)m[0][0], (double)m[0][1], (double)m[0][2], (double)m[1][0], (double)m[1][1], (double)m[1][2], (double)m[2][0], (double)m[2][1], (double)m[2][2]
+#define PRINT_M4_FORMAT "((%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f))"
+#define PRINT_M4_ARGS(m) (double)m[0][0], (double)m[0][1], (double)m[0][2], (double)m[0][3], (double)m[1][0], (double)m[1][1], (double)m[1][2], (double)m[1][3], \
+ (double)m[2][0], (double)m[2][1], (double)m[2][2], (double)m[2][3], (double)m[3][0], (double)m[3][1], (double)m[3][2], (double)m[3][3]
+#endif
+
+AbcStrandsChildrenReader::AbcStrandsChildrenReader(StrandsChildren *strands) :
+ m_strands(strands)
+{
+}
+
+AbcStrandsChildrenReader::~AbcStrandsChildrenReader()
+{
+ discard_result();
+}
+
+void AbcStrandsChildrenReader::init_abc(IObject object)
+{
+ if (m_curves)
+ return;
+ m_curves = ICurves(object, kWrapExisting);
+
+ ICurvesSchema &schema = m_curves.getSchema();
+ ICompoundProperty geom_props = schema.getArbGeomParams();
+ ICompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_root_rot = IQuatfArrayProperty(user_props, "root_rotations");
+ m_prop_root_positions = IV3fArrayProperty(user_props, "root_positions");
+ if (geom_props.getPropertyHeader("cutoff"))
+ m_param_cutoff = IFloatGeomParam(geom_props, "cutoff");
+ m_param_times = IFloatGeomParam(geom_props, "times");
+ m_prop_parents = IInt32ArrayProperty(user_props, "parents", 0);
+ m_prop_parent_weights = IFloatArrayProperty(user_props, "parent_weights", 0);
+ m_prop_curve_uvs = IV2fArrayProperty(user_props, "curve_uvs", 0);
+ m_prop_curve_vcols = IC3fArrayProperty(user_props, "curve_vcols", 0);
+}
+
+PTCReadSampleResult AbcStrandsChildrenReader::read_sample_abc(chrono_t time)
+{
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ if (!m_curves.valid()) {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ ICurvesSchema &schema = m_curves.getSchema();
+ if (schema.getNumSamples() == 0) {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ ICurvesSchema::Sample sample;
+ schema.get(sample, ss);
+
+ P3fArraySamplePtr sample_co = sample.getPositions();
+ Int32ArraySamplePtr sample_numvert = sample.getCurvesNumVertices();
+ QuatfArraySamplePtr sample_root_rotations = abc_interpolate_sample_linear(m_prop_root_rot, time);
+ V3fArraySamplePtr sample_root_positions = abc_interpolate_sample_linear(m_prop_root_positions, time);
+ IFloatGeomParam::Sample sample_cutoff;
+ if (m_param_cutoff)
+ sample_cutoff = m_param_cutoff.getExpandedValue(ss);
+ IFloatGeomParam::Sample sample_time = m_param_times.getExpandedValue(ss);
+ Int32ArraySamplePtr sample_parents = m_prop_parents.getValue(ss);
+ FloatArraySamplePtr sample_parent_weights = m_prop_parent_weights.getValue(ss);
+ V2fArraySamplePtr sample_curve_uvs = m_prop_curve_uvs.getValue(ss);
+ C3fArraySamplePtr sample_curve_vcols = m_prop_curve_vcols.getValue(ss);
+
+ if (!sample_co || !sample_numvert) {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ int totcurves = sample_numvert->size();
+ int totverts = sample_co->size();
+
+ if (!totcurves) {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ if (sample_root_rotations->size() != totcurves ||
+ sample_root_positions->size() != totcurves ||
+ sample_parents->size() != 4 * totcurves ||
+ sample_parent_weights->size() != 4 * totcurves)
+ return PTC_READ_SAMPLE_INVALID;
+
+ if (m_strands && (m_strands->totcurves != totcurves || m_strands->totverts != totverts))
+ m_strands = NULL;
+ if (!m_strands)
+ m_strands = BKE_strands_children_new(totcurves, totverts);
+
+ const int32_t *numvert = sample_numvert->get();
+ const Quatf *root_rot = sample_root_rotations->get();
+ const V3f *root_positions = sample_root_positions->get();
+ const float32_t *cutoff = sample_cutoff ? sample_cutoff.getVals()->get() : NULL;
+ const int32_t *parents = sample_parents->get();
+ const float32_t *parent_weights = sample_parent_weights->get();
+ for (int i = 0; i < sample_numvert->size(); ++i) {
+ StrandsChildCurve *scurve = &m_strands->curves[i];
+ scurve->numverts = *numvert;
+ scurve->cutoff = -1.0f;
+
+ float qt[4] = {root_rot->r, root_rot->v.x, root_rot->v.y, root_rot->v.z};
+ quat_to_mat4(scurve->root_matrix, qt);
+ copy_v3_v3(scurve->root_matrix[3], root_positions->getValue());
+
+ scurve->cutoff = cutoff ? *cutoff : -1.0f;
+
+ scurve->parents[0] = parents[0];
+ scurve->parents[1] = parents[1];
+ scurve->parents[2] = parents[2];
+ scurve->parents[3] = parents[3];
+ scurve->parent_weights[0] = parent_weights[0];
+ scurve->parent_weights[1] = parent_weights[1];
+ scurve->parent_weights[2] = parent_weights[2];
+ scurve->parent_weights[3] = parent_weights[3];
+
+ ++numvert;
+ ++root_rot;
+ ++root_positions;
+ parents += 4;
+ parent_weights += 4;
+ if (cutoff) ++cutoff;
+ }
+
+ if (sample_curve_uvs->size() > 0 && sample_curve_uvs->size() % totcurves == 0) {
+ int num_layers = sample_curve_uvs->size() / totcurves;
+
+ const V2f *uvs = sample_curve_uvs->get();
+
+ BKE_strands_children_add_uvs(m_strands, num_layers);
+
+ StrandsChildCurveUV *scurve_uv = m_strands->curve_uvs;
+ for (int j = 0; j < num_layers; ++j) {
+ for (int i = 0; i < totcurves; ++i) {
+ copy_v2_v2(scurve_uv->uv, uvs->getValue());
+
+ ++uvs;
+ ++scurve_uv;
+ }
+ }
+ }
+
+ if (sample_curve_vcols->size() > 0 && sample_curve_vcols->size() % totcurves == 0) {
+ int num_layers = sample_curve_vcols->size() / totcurves;
+
+ const C3f *vcols = sample_curve_vcols->get();
+
+ BKE_strands_children_add_vcols(m_strands, num_layers);
+
+ StrandsChildCurveVCol *scurve_vcol = m_strands->curve_vcols;
+ for (int j = 0; j < num_layers; ++j) {
+ for (int i = 0; i < totcurves; ++i) {
+ copy_v3_v3(scurve_vcol->vcol, vcols->getValue());
+
+ ++vcols;
+ ++scurve_vcol;
+ }
+ }
+ }
+
+ const V3f *co = sample_co->get();
+ const float32_t *curve_time = sample_time.getVals()->get();
+ for (int i = 0; i < sample_co->size(); ++i) {
+ StrandsChildVertex *svert = &m_strands->verts[i];
+ copy_v3_v3(svert->co, co->getValue());
+ copy_v3_v3(svert->base, svert->co);
+ svert->time = *curve_time;
+
+ ++co;
+ ++curve_time;
+ }
+
+ BKE_strands_children_ensure_normals(m_strands);
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+StrandsChildren *AbcStrandsChildrenReader::acquire_result()
+{
+ StrandsChildren *strands = m_strands;
+ m_strands = NULL;
+ return strands;
+}
+
+void AbcStrandsChildrenReader::discard_result()
+{
+ BKE_strands_children_free(m_strands);
+ m_strands = NULL;
+}
+
+
+AbcStrandsReader::AbcStrandsReader(Strands *strands, StrandsChildren *children, bool read_motion, bool read_children) :
+ m_read_motion(read_motion),
+ m_read_children(read_children),
+ m_strands(strands),
+ m_child_reader(children)
+{
+}
+
+AbcStrandsReader::~AbcStrandsReader()
+{
+ discard_result();
+}
+
+void AbcStrandsReader::init(ReaderArchive *archive)
+{
+ AbcReader::init(archive);
+ m_child_reader.init(archive);
+}
+
+void AbcStrandsReader::init_abc(IObject object)
+{
+ if (m_curves)
+ return;
+ m_curves = ICurves(object, kWrapExisting);
+
+ ICurvesSchema &schema = m_curves.getSchema();
+ ICompoundProperty geom_props = schema.getArbGeomParams();
+
+ m_param_root_rot = IQuatfGeomParam(geom_props, "root_rotations");
+ m_param_root_orig_verts = IUInt32GeomParam(geom_props, "root_orig_verts");
+ m_param_root_orig_weights = IFloatGeomParam(geom_props, "root_orig_weights");
+ m_param_root_orig_poly = IInt32GeomParam(geom_props, "root_orig_poly");
+ m_param_root_orig_loops = IUInt32GeomParam(geom_props, "root_orig_loops");
+
+ m_param_times = IFloatGeomParam(geom_props, "times");
+ m_param_weights = IFloatGeomParam(geom_props, "weights");
+
+ if (m_read_motion && geom_props.getPropertyHeader("motion_state")) {
+ m_param_motion_state = ICompoundProperty(geom_props, "motion_state");
+ m_param_motion_co = IP3fGeomParam(m_param_motion_state, "position");
+ m_param_motion_vel = IV3fGeomParam(m_param_motion_state, "velocity");
+ }
+
+ if (m_read_children && m_curves.getChildHeader("children")) {
+ IObject child = m_curves.getChild("children");
+ m_child_reader.init_abc(child);
+ }
+}
+
+PTCReadSampleResult AbcStrandsReader::read_sample_abc(chrono_t time)
+{
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ if (!m_curves.valid())
+ return PTC_READ_SAMPLE_INVALID;
+
+ ICurvesSchema &schema = m_curves.getSchema();
+ if (schema.getNumSamples() == 0)
+ return PTC_READ_SAMPLE_INVALID;
+
+ ICurvesSchema::Sample sample, sample_base;
+ schema.get(sample, ss);
+ schema.get(sample_base, ISampleSelector((index_t)0));
+
+ P3fArraySamplePtr sample_co = sample.getPositions();
+ P3fArraySamplePtr sample_co_base = sample_base.getPositions();
+ Int32ArraySamplePtr sample_numvert = sample.getCurvesNumVertices();
+ IQuatfGeomParam::Sample sample_root_rotations = m_param_root_rot.getExpandedValue(ss);
+ IUInt32GeomParam::Sample sample_root_orig_verts = m_param_root_orig_verts.getExpandedValue(ss);
+ IFloatGeomParam::Sample sample_root_orig_weights = m_param_root_orig_weights.getExpandedValue(ss);
+ IInt32GeomParam::Sample sample_root_orig_poly = m_param_root_orig_poly.getExpandedValue(ss);
+ IUInt32GeomParam::Sample sample_root_orig_loops = m_param_root_orig_loops.getExpandedValue(ss);
+ IQuatfGeomParam::Sample sample_root_rotations_base = m_param_root_rot.getExpandedValue(ISampleSelector((index_t)0));
+ IFloatGeomParam::Sample sample_time = m_param_times.getExpandedValue(ss);
+ IFloatGeomParam::Sample sample_weight = m_param_weights.getExpandedValue(ss);
+
+ if (!sample_co || !sample_numvert || !sample_co_base || sample_co_base->size() != sample_co->size())
+ return PTC_READ_SAMPLE_INVALID;
+
+ if (m_strands && (m_strands->totcurves != sample_numvert->size() || m_strands->totverts != sample_co->size()))
+ m_strands = NULL;
+ if (!m_strands)
+ m_strands = BKE_strands_new(sample_numvert->size(), sample_co->size());
+
+ const int32_t *numvert = sample_numvert->get();
+ const Quatf *root_rot = sample_root_rotations.getVals()->get();
+ const uint32_t *orig_verts = sample_root_orig_verts.getVals()->get();
+ const float32_t *orig_weights = sample_root_orig_weights.getVals()->get();
+ const int32_t *orig_poly = sample_root_orig_poly.getVals()->get();
+ const uint32_t *orig_loops = sample_root_orig_loops.getVals()->get();
+ for (int i = 0; i < sample_numvert->size(); ++i) {
+ StrandsCurve *scurve = &m_strands->curves[i];
+ scurve->numverts = *numvert;
+ float qt[4] = {root_rot->r, root_rot->v.x, root_rot->v.y, root_rot->v.z};
+ quat_to_mat3(scurve->root_matrix, qt);
+ scurve->msurf.orig_verts[0] = orig_verts[0];
+ scurve->msurf.orig_verts[1] = orig_verts[1];
+ scurve->msurf.orig_verts[2] = orig_verts[2];
+ scurve->msurf.orig_weights[0] = orig_weights[0];
+ scurve->msurf.orig_weights[1] = orig_weights[1];
+ scurve->msurf.orig_weights[2] = orig_weights[2];
+ scurve->msurf.orig_poly = *orig_poly;
+ scurve->msurf.orig_loops[0] = orig_loops[0];
+ scurve->msurf.orig_loops[1] = orig_loops[1];
+ scurve->msurf.orig_loops[2] = orig_loops[2];
+
+ ++numvert;
+ ++root_rot;
+ orig_verts += 3;
+ orig_weights += 3;
+ orig_poly += 1;
+ orig_loops += 3;
+ }
+
+ const V3f *co = sample_co->get();
+ const float32_t *curve_time = sample_time.getVals()->get();
+ const float32_t *weight = sample_weight.getVals()->get();
+ for (int i = 0; i < sample_co->size(); ++i) {
+ StrandsVertex *svert = &m_strands->verts[i];
+ copy_v3_v3(svert->co, co->getValue());
+ svert->time = *curve_time;
+ svert->weight = *weight;
+
+ ++co;
+ ++curve_time;
+ ++weight;
+ }
+
+ /* Correction for base coordinates: these are in object space of frame 1,
+ * but we want the relative shape. Offset them to the current root location.
+ */
+ const Quatf *root_rot_base = sample_root_rotations_base.getVals()->get();
+ const V3f *co_base = sample_co_base->get();
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, m_strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ if (it_strand.curve->numverts <= 0)
+ continue;
+
+ float hairmat_base[4][4];
+ float qt[4] = {root_rot_base->r, root_rot_base->v.x, root_rot_base->v.y, root_rot_base->v.z};
+ quat_to_mat4(hairmat_base, qt);
+ copy_v3_v3(hairmat_base[3], co_base[0].getValue());
+
+ float hairmat[4][4];
+ copy_m4_m3(hairmat, it_strand.curve->root_matrix);
+ copy_v3_v3(hairmat[3], it_strand.verts[0].co);
+
+ float mat[4][4];
+ invert_m4_m4(mat, hairmat_base);
+ mul_m4_m4m4(mat, hairmat, mat);
+
+ StrandVertexIterator it_vert;
+ for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+// mul_v3_m4v3(it_vert.vertex->base, mat, co_base->getValue());
+ copy_v3_v3(it_vert.vertex->base, it_vert.vertex->co);
+
+ ++co_base;
+ }
+
+ ++root_rot_base;
+ }
+
+ if (m_read_motion &&
+ m_param_motion_co && m_param_motion_co.getNumSamples() > 0 &&
+ m_param_motion_vel && m_param_motion_vel.getNumSamples() > 0)
+ {
+ IP3fGeomParam::Sample sample_motion_co = m_param_motion_co.getExpandedValue(ss);
+ IV3fGeomParam::Sample sample_motion_vel = m_param_motion_vel.getExpandedValue(ss);
+
+ const V3f *co = sample_motion_co.getVals()->get();
+ const V3f *vel = sample_motion_vel.getVals()->get();
+ if (co && vel) {
+ BKE_strands_add_motion_state(m_strands);
+
+ for (int i = 0; i < m_strands->totverts; ++i) {
+ StrandsMotionState *ms = &m_strands->state[i];
+ copy_v3_v3(ms->co, co->getValue());
+ copy_v3_v3(ms->vel, vel->getValue());
+
+ ++co;
+ ++vel;
+ }
+ }
+ }
+
+ BKE_strands_ensure_normals(m_strands);
+
+ if (m_read_children) {
+ m_child_reader.read_sample_abc(time);
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+Strands *AbcStrandsReader::acquire_result()
+{
+ Strands *strands = m_strands;
+ m_strands = NULL;
+ return strands;
+}
+
+void AbcStrandsReader::discard_result()
+{
+ BKE_strands_free(m_strands);
+ m_strands = NULL;
+}
+
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_particles.h b/source/blender/pointcache/alembic/abc_particles.h
new file mode 100644
index 00000000000..26d1bce78bd
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_particles.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_PARTICLES_H
+#define PTC_ABC_PARTICLES_H
+
+#include <Alembic/AbcGeom/IPoints.h>
+#include <Alembic/AbcGeom/OPoints.h>
+#include <Alembic/AbcGeom/ICurves.h>
+#include <Alembic/AbcGeom/OCurves.h>
+
+#include "ptc_types.h"
+
+#include "PTC_api.h"
+
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+#include "abc_cloth.h"
+
+struct ListBase;
+struct Object;
+struct ParticleSystem;
+struct ParticleCacheKey;
+struct Strands;
+struct StrandsChildren;
+
+namespace PTC {
+
+class AbcDerivedMeshWriter;
+class AbcDerivedMeshReader;
+
+
+class AbcHairChildrenWriter : public ParticlesWriter, public AbcWriter {
+public:
+ AbcHairChildrenWriter(const std::string &name, Object *ob, ParticleSystem *psys);
+ ~AbcHairChildrenWriter();
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ ParticleSystemModifierData *m_psmd;
+
+ AbcGeom::OCurves m_curves;
+ AbcGeom::OQuatfArrayProperty m_prop_root_rot;
+ AbcGeom::OV3fArrayProperty m_prop_root_positions;
+ AbcGeom::OFloatGeomParam m_param_cutoff;
+ AbcGeom::OFloatGeomParam m_param_times;
+ AbcGeom::OInt32ArrayProperty m_prop_parents;
+ AbcGeom::OFloatArrayProperty m_prop_parent_weights;
+ AbcGeom::OV2fArrayProperty m_prop_curve_uvs;
+ AbcGeom::OC3fArrayProperty m_prop_curve_vcols;
+};
+
+
+class AbcHairWriter : public ParticlesWriter, public AbcWriter {
+public:
+ AbcHairWriter(const std::string &name, Object *ob, ParticleSystem *psys);
+ ~AbcHairWriter();
+
+ void init(WriterArchive *archive);
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ ParticleSystemModifierData *m_psmd;
+
+ AbcGeom::OCurves m_curves;
+ AbcGeom::OQuatfGeomParam m_param_root_rot;
+ AbcGeom::OUInt32GeomParam m_param_root_orig_verts;
+ AbcGeom::OFloatGeomParam m_param_root_orig_weights;
+ AbcGeom::OInt32GeomParam m_param_root_orig_poly;
+ AbcGeom::OUInt32GeomParam m_param_root_orig_loops;
+ AbcGeom::OFloatGeomParam m_param_times;
+ AbcGeom::OFloatGeomParam m_param_weights;
+
+ AbcHairChildrenWriter m_child_writer;
+};
+
+
+class AbcStrandsChildrenWriter : public AbcWriter {
+public:
+ AbcStrandsChildrenWriter(const std::string &name, const std::string &abc_name, DupliObjectData *dobdata);
+
+ StrandsChildren *get_strands() const;
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ std::string m_name;
+ std::string m_abc_name;
+ DupliObjectData *m_dobdata;
+
+ AbcGeom::OCurves m_curves;
+ AbcGeom::OQuatfArrayProperty m_prop_root_rot;
+ AbcGeom::OV3fArrayProperty m_prop_root_positions;
+ AbcGeom::OFloatGeomParam m_param_cutoff;
+ AbcGeom::OFloatGeomParam m_param_times;
+ AbcGeom::OInt32ArrayProperty m_prop_parents;
+ AbcGeom::OFloatArrayProperty m_prop_parent_weights;
+ AbcGeom::OV2fArrayProperty m_prop_curve_uvs;
+ AbcGeom::OC3fArrayProperty m_prop_curve_vcols;
+};
+
+
+class AbcStrandsWriter : public AbcWriter {
+public:
+ AbcStrandsWriter(const std::string &name, DupliObjectData *dobdata);
+
+ Strands *get_strands() const;
+
+ void init(WriterArchive *archive);
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ std::string m_name;
+ DupliObjectData *m_dobdata;
+
+ AbcGeom::OCurves m_curves;
+ AbcGeom::OQuatfGeomParam m_param_root_rot;
+ AbcGeom::OUInt32GeomParam m_param_root_orig_verts;
+ AbcGeom::OFloatGeomParam m_param_root_orig_weights;
+ AbcGeom::OInt32GeomParam m_param_root_orig_poly;
+ AbcGeom::OUInt32GeomParam m_param_root_orig_loops;
+ AbcGeom::OFloatGeomParam m_param_times;
+ AbcGeom::OFloatGeomParam m_param_weights;
+ AbcGeom::OCompoundProperty m_param_motion_state;
+ AbcGeom::OP3fGeomParam m_param_motion_co;
+ AbcGeom::OV3fGeomParam m_param_motion_vel;
+
+ AbcStrandsChildrenWriter m_child_writer;
+};
+
+
+class AbcStrandsChildrenReader : public AbcReader {
+public:
+ AbcStrandsChildrenReader(StrandsChildren *strands);
+ ~AbcStrandsChildrenReader();
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+ StrandsChildren *get_result() { return m_strands; }
+ StrandsChildren *acquire_result();
+ void discard_result();
+
+private:
+ StrandsChildren *m_strands;
+
+ AbcGeom::ICurves m_curves;
+ AbcGeom::IQuatfArrayProperty m_prop_root_rot;
+ AbcGeom::IV3fArrayProperty m_prop_root_positions;
+ AbcGeom::IFloatGeomParam m_param_cutoff;
+ AbcGeom::IFloatGeomParam m_param_times;
+ AbcGeom::IInt32ArrayProperty m_prop_parents;
+ AbcGeom::IFloatArrayProperty m_prop_parent_weights;
+ AbcGeom::IV2fArrayProperty m_prop_curve_uvs;
+ AbcGeom::IC3fArrayProperty m_prop_curve_vcols;
+};
+
+
+class AbcStrandsReader : public AbcReader {
+public:
+ AbcStrandsReader(Strands *strands, StrandsChildren *children, bool read_motion, bool read_children);
+ ~AbcStrandsReader();
+
+ void init(ReaderArchive *archive);
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+ Strands *acquire_result();
+ void discard_result();
+
+ AbcStrandsChildrenReader &child_reader() { return m_child_reader; }
+
+private:
+ bool m_read_motion, m_read_children;
+ Strands *m_strands;
+
+ AbcGeom::ICurves m_curves;
+ AbcGeom::IQuatfGeomParam m_param_root_rot;
+ AbcGeom::IUInt32GeomParam m_param_root_orig_verts;
+ AbcGeom::IFloatGeomParam m_param_root_orig_weights;
+ AbcGeom::IInt32GeomParam m_param_root_orig_poly;
+ AbcGeom::IUInt32GeomParam m_param_root_orig_loops;
+ AbcGeom::IFloatGeomParam m_param_times;
+ AbcGeom::IFloatGeomParam m_param_weights;
+ AbcGeom::ICompoundProperty m_param_motion_state;
+ AbcGeom::IP3fGeomParam m_param_motion_co;
+ AbcGeom::IV3fGeomParam m_param_motion_vel;
+
+ AbcStrandsChildrenReader m_child_reader;
+};
+
+
+} /* namespace PTC */
+
+#endif /* PTC_PARTICLES_H */
diff --git a/source/blender/pointcache/alembic/abc_reader.cpp b/source/blender/pointcache/alembic/abc_reader.cpp
new file mode 100644
index 00000000000..17f574af7ac
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_reader.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+//#include <Alembic/AbcCoreHDF5/ReadWrite.h>
+#include <Alembic/AbcCoreOgawa/ReadWrite.h>
+#include <Alembic/Abc/ArchiveInfo.h>
+#include <Alembic/Abc/IArchive.h>
+#include <Alembic/Abc/IObject.h>
+
+#include "alembic.h"
+#include "abc_reader.h"
+
+#include "util_error_handler.h"
+
+extern "C" {
+#include "DNA_ID.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+
+AbcReaderArchive *AbcReaderArchive::open(double fps, float start_frame, const std::string &filename, ErrorHandler *error_handler)
+{
+ IArchive abc_archive;
+ PTC_SAFE_CALL_BEGIN
+// abc_archive = IArchive(AbcCoreHDF5::ReadArchive(), filename, Abc::ErrorHandler::kThrowPolicy);
+ abc_archive = IArchive(AbcCoreOgawa::ReadArchive(), filename, Abc::ErrorHandler::kThrowPolicy);
+ PTC_SAFE_CALL_END_HANDLER(error_handler)
+
+ if (abc_archive)
+ return new AbcReaderArchive(fps, start_frame, error_handler, abc_archive);
+ else
+ return NULL;
+}
+
+AbcReaderArchive::AbcReaderArchive(double fps, float start_frame, ErrorHandler *error_handler, IArchive abc_archive) :
+ FrameMapper(fps, start_frame),
+ m_error_handler(error_handler),
+ m_use_render(false),
+ m_abc_archive(abc_archive)
+{
+ if (m_abc_archive.getTop().getChildHeader("root"))
+ m_abc_root = IObject(m_abc_archive.getTop(), "root");
+ else
+ m_abc_root = IObject();
+
+ if (m_abc_archive.getTop().getChildHeader("root_render"))
+ m_abc_root_render = IObject(m_abc_archive.getTop(), "root_render");
+ else
+ m_abc_root_render = IObject();
+}
+
+AbcReaderArchive::~AbcReaderArchive()
+{
+}
+
+PTCArchiveResolution AbcReaderArchive::get_resolutions()
+{
+ int res = 0;
+ if (m_abc_root)
+ res |= PTC_RESOLUTION_PREVIEW;
+ if (m_abc_root_render)
+ res |= PTC_RESOLUTION_RENDER;
+ return (PTCArchiveResolution)res;
+}
+
+Abc::IObject AbcReaderArchive::root()
+{
+ if (m_use_render)
+ return m_abc_root_render ? m_abc_root_render : m_abc_root;
+ else
+ return m_abc_root ? m_abc_root : m_abc_root_render;
+}
+
+IObject AbcReaderArchive::get_id_object(ID *id)
+{
+ if (!m_abc_archive)
+ return IObject();
+
+ IObject root = this->root();
+ return root.getChild(id->name);
+}
+
+bool AbcReaderArchive::has_id_object(ID *id)
+{
+ if (!m_abc_archive)
+ return false;
+
+ IObject root = this->root();
+ return root.getChild(id->name).valid();
+}
+
+bool AbcReaderArchive::get_frame_range(int &start_frame, int &end_frame)
+{
+ if (m_abc_archive) {
+ double start_time, end_time;
+ GetArchiveStartAndEndTime(m_abc_archive, start_time, end_time);
+ start_frame = (int)time_to_frame(start_time);
+ end_frame = (int)time_to_frame(end_time);
+ return true;
+ }
+ else {
+ start_frame = end_frame = 1;
+ return false;
+ }
+}
+
+void AbcReaderArchive::get_info_stream(void (*stream)(void *, const char *), void *userdata)
+{
+ if (m_abc_archive)
+ abc_archive_info_stream(m_abc_archive, stream, userdata);
+}
+
+void AbcReaderArchive::get_info(CacheArchiveInfo *info, IDProperty *metadata)
+{
+ if (m_abc_archive)
+ abc_archive_info_nodes(m_abc_archive, info, metadata, false, false);
+}
+
+void AbcReaderArchive::get_info_nodes(CacheArchiveInfo *info, bool calc_bytes_size)
+{
+ if (m_abc_archive)
+ abc_archive_info_nodes(m_abc_archive, info, NULL, true, calc_bytes_size);
+}
+
+ISampleSelector AbcReaderArchive::get_frame_sample_selector(float frame)
+{
+ return ISampleSelector(frame_to_time(frame), ISampleSelector::kFloorIndex);
+}
+
+ISampleSelector AbcReaderArchive::get_frame_sample_selector(chrono_t time)
+{
+ return ISampleSelector(time, ISampleSelector::kFloorIndex);
+}
+
+
+bool AbcReader::get_frame_range(int &start_frame, int &end_frame)
+{
+ return m_abc_archive->get_frame_range(start_frame, end_frame);
+}
+
+PTCReadSampleResult AbcReader::test_sample(float frame)
+{
+ if (m_abc_archive) {
+ int start_frame, end_frame;
+ m_abc_archive->get_frame_range(start_frame, end_frame);
+
+ if (frame < start_frame)
+ return PTC_READ_SAMPLE_EARLY;
+ else if (frame > end_frame)
+ return PTC_READ_SAMPLE_LATE;
+ else {
+ /* TODO could also be EXACT, but INTERPOLATED is more general
+ * do we need to support this?
+ * checking individual time samplings is also possible, but more involved.
+ */
+ return PTC_READ_SAMPLE_INTERPOLATED;
+ }
+ }
+ else {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+}
+
+PTCReadSampleResult AbcReader::read_sample(float frame)
+{
+
+ try {
+ return read_sample_abc(m_abc_archive->frame_to_time(frame));
+ }
+ catch (Alembic::Util::Exception e) {
+ handle_alembic_exception(ErrorHandler::get_default_handler(), PTC_ERROR_CRITICAL, e);
+ return PTC_READ_SAMPLE_INVALID;
+ }
+ return PTC_READ_SAMPLE_INVALID;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_reader.h b/source/blender/pointcache/alembic/abc_reader.h
new file mode 100644
index 00000000000..86cfdd91d51
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_reader.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_READER_H
+#define PTC_ABC_READER_H
+
+#include <string>
+
+#include <Alembic/Abc/IArchive.h>
+#include <Alembic/Abc/IObject.h>
+#include <Alembic/Abc/ISampleSelector.h>
+
+#include "reader.h"
+
+#include "abc_frame_mapper.h"
+
+#include "util_error_handler.h"
+#include "util_types.h"
+
+namespace PTC {
+
+using namespace Alembic;
+
+using Abc::chrono_t;
+
+class AbcReaderArchive : public ReaderArchive, public FrameMapper {
+public:
+ virtual ~AbcReaderArchive();
+
+ static AbcReaderArchive *open(double fps, float start_frame, const std::string &filename, ErrorHandler *error_handler);
+
+ PTCArchiveResolution get_resolutions();
+ bool use_render() const { return m_use_render; }
+ void use_render(bool enable) { m_use_render = enable; }
+
+ Abc::IArchive abc_archive() const { return m_abc_archive; }
+ Abc::IObject root();
+
+ Abc::IObject get_id_object(ID *id);
+ bool has_id_object(ID *id);
+
+ bool get_frame_range(int &start_frame, int &end_frame);
+ Abc::ISampleSelector get_frame_sample_selector(float frame);
+ Abc::ISampleSelector get_frame_sample_selector(chrono_t time);
+
+ void get_info_stream(void (*stream)(void *, const char *), void *userdata);
+ void get_info(CacheArchiveInfo *info, IDProperty *metadata);
+ void get_info_nodes(CacheArchiveInfo *info, bool calc_bytes_size);
+
+protected:
+ AbcReaderArchive(double fps, float start_frame, ErrorHandler *error_handler, Abc::IArchive abc_archive);
+
+protected:
+ ErrorHandler *m_error_handler;
+ bool m_use_render;
+
+ Abc::IArchive m_abc_archive;
+ Abc::IObject m_abc_root;
+ Abc::IObject m_abc_root_render;
+};
+
+class AbcReader : public Reader {
+public:
+ AbcReader() :
+ m_abc_archive(0)
+ {}
+
+ void init(ReaderArchive *archive)
+ {
+ BLI_assert(dynamic_cast<AbcReaderArchive*>(archive));
+ m_abc_archive = static_cast<AbcReaderArchive*>(archive);
+ }
+
+ virtual void init_abc(Abc::IObject /*object*/) {}
+
+ AbcReaderArchive *abc_archive() const { return m_abc_archive; }
+
+ bool get_frame_range(int &start_frame, int &end_frame);
+
+ Abc::ISampleSelector get_frame_sample_selector(float frame) { return m_abc_archive->get_frame_sample_selector(frame); }
+ Abc::ISampleSelector get_frame_sample_selector(chrono_t time) { return m_abc_archive->get_frame_sample_selector(time); }
+
+ PTCReadSampleResult test_sample(float frame);
+ PTCReadSampleResult read_sample(float frame);
+ virtual PTCReadSampleResult read_sample_abc(chrono_t time) = 0;
+
+private:
+ AbcReaderArchive *m_abc_archive;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_READER_H */
diff --git a/source/blender/pointcache/alembic/abc_schema.h b/source/blender/pointcache/alembic/abc_schema.h
new file mode 100644
index 00000000000..d8d70ddac05
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_schema.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_SCHEMA_H
+#define PTC_ABC_SCHEMA_H
+
+#include <Alembic/AbcGeom/SchemaInfoDeclarations.h>
+#include <Alembic/AbcGeom/IGeomBase.h>
+#include <Alembic/AbcGeom/OGeomBase.h>
+
+namespace PTC {
+
+#if 0
+#define PTC_SCHEMA_INFO ALEMBIC_ABCGEOM_DECLARE_SCHEMA_INFO
+
+using namespace Alembic::AbcGeom;
+#endif
+
+
+#if 0
+/* XXX We define an extended schema class to implement the wrapper constructor.
+ * This was removed in Alembic 1.1 for some reason ...
+ */
+template <class SCHEMA>
+class OSchemaObject : public Abc::OSchemaObject<SCHEMA>
+{
+public:
+ //! The default constructor creates an empty OSchemaObject function set.
+ //! ...
+ OSchemaObject() : Abc::OSchemaObject<SCHEMA>() {}
+
+ //! The primary constructor creates an OSchemaObject as a child of the
+ //! first argument, which is any Abc or AbcCoreAbstract (or other)
+ //! object which can be intrusively cast to an ObjectWriterPtr.
+ template <class OBJECT_PTR>
+ OSchemaObject(OBJECT_PTR iParentObject,
+ const std::string &iName,
+
+ const Argument &iArg0 = Argument(),
+ const Argument &iArg1 = Argument(),
+ const Argument &iArg2 = Argument())
+ : Abc::OSchemaObject<SCHEMA>(iParentObject, iName, iArg0, iArg1, iArg2)
+ {}
+
+ //! Wrap an existing schema object.
+ //! ...
+ template <class OBJECT_PTR>
+ OSchemaObject(OBJECT_PTR iThisObject,
+ WrapExistingFlag iFlag,
+ const Argument &iArg0 = Argument(),
+ const Argument &iArg1 = Argument(),
+ const Argument &iArg2 = Argument() );
+};
+
+//-*****************************************************************************
+template<class SCHEMA>
+template<class OBJECT_PTR>
+inline OSchemaObject<SCHEMA>::OSchemaObject(
+ OBJECT_PTR iObject,
+ WrapExistingFlag iFlag,
+ const Argument &iArg0,
+ const Argument &iArg1,
+ const Argument &iArg2 )
+ : OObject(iObject,
+ iFlag,
+ GetErrorHandlerPolicy(iObject,
+ iArg0, iArg1, iArg2))
+{
+ ALEMBIC_ABC_SAFE_CALL_BEGIN("OSchemaObject::OSchemaObject( wrap )");
+
+ const AbcA::ObjectHeader &oheader = this->getHeader();
+
+ Abc::OSchemaObject<SCHEMA>::m_schema = SCHEMA(
+ this->getProperties().getProperty(SCHEMA::getDefaultSchemaName()).getPtr()->asCompoundPtr(),
+ iFlag,
+ this->getErrorHandlerPolicy(),
+ GetSchemaInterpMatching(iArg0, iArg1, iArg2));
+
+ /* XXX TODO gives compiler error atm */
+// ABCA_ASSERT(matches(oheader, GetSchemaInterpMatching(iArg0, iArg1, iArg2)),
+// "Incorrect match of schema: "
+// << oheader.getMetaData().get( "schemaObjTitle" )
+// << " to expected: "
+// << getSchemaObjTitle());
+
+ ALEMBIC_ABC_SAFE_CALL_END_RESET();
+}
+#endif
+
+} /* namespace PTC */
+
+#endif /* PTC_SCHEMA_H */
diff --git a/source/blender/pointcache/alembic/abc_simdebug.cpp b/source/blender/pointcache/alembic/abc_simdebug.cpp
new file mode 100644
index 00000000000..191b2856dc9
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_simdebug.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2014, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "abc_simdebug.h"
+
+extern "C" {
+#include "BLI_ghash.h"
+#include "BLI_math.h"
+}
+
+#include "PTC_api.h"
+
+namespace PTC {
+
+using namespace Abc;
+
+struct SimDebugSample {
+ std::vector<uint32_t> category_hash;
+ std::vector<uint32_t> hash;
+
+ std::vector<int32_t> type;
+ std::vector<C3f> color;
+ std::vector<V3f> v1;
+ std::vector<V3f> v2;
+};
+
+AbcSimDebugWriter::AbcSimDebugWriter(const std::string &name, SimDebugData *data) :
+ m_name(name),
+ m_data(data)
+{
+}
+
+AbcSimDebugWriter::~AbcSimDebugWriter()
+{
+}
+
+void AbcSimDebugWriter::init_abc(OObject parent)
+{
+ if (m_object)
+ return;
+
+ m_object = OObject(parent, m_name, abc_archive()->frame_sampling_index());
+ OCompoundProperty props = m_object.getProperties();
+
+ m_prop_category_hash = OUInt32ArrayProperty(props, "category_hash", abc_archive()->frame_sampling_index());
+ m_prop_hash = OUInt32ArrayProperty(props, "hash", abc_archive()->frame_sampling_index());
+ m_prop_type = OInt32ArrayProperty(props, "type", abc_archive()->frame_sampling_index());
+ m_prop_color = OC3fArrayProperty(props, "color", abc_archive()->frame_sampling_index());
+ m_prop_v1 = OV3fArrayProperty(props, "v1", abc_archive()->frame_sampling_index());
+ m_prop_v2 = OV3fArrayProperty(props, "v2", abc_archive()->frame_sampling_index());
+}
+
+static void create_sample(SimDebugData *data, SimDebugSample &sample)
+{
+ int numelem = BLI_ghash_size(data->gh);
+ GHashIterator iter;
+
+ sample.category_hash.reserve(numelem);
+ sample.hash.reserve(numelem);
+ sample.type.reserve(numelem);
+ sample.color.reserve(numelem);
+ sample.v1.reserve(numelem);
+ sample.v2.reserve(numelem);
+
+ for (BLI_ghashIterator_init(&iter, data->gh); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) {
+ SimDebugElement *elem = (SimDebugElement *)BLI_ghashIterator_getValue(&iter);
+
+ sample.category_hash.push_back(elem->category_hash);
+ sample.hash.push_back(elem->hash);
+ sample.type.push_back(elem->type);
+ sample.color.push_back(C3f(elem->color[0], elem->color[1], elem->color[2]));
+ sample.v1.push_back(V3f(elem->v1[0], elem->v1[1], elem->v1[2]));
+ sample.v2.push_back(V3f(elem->v2[0], elem->v2[1], elem->v2[2]));
+ }
+}
+
+void AbcSimDebugWriter::write_sample()
+{
+ if (!m_object)
+ return;
+
+ SimDebugSample sample;
+
+ create_sample(m_data, sample);
+
+ m_prop_category_hash.set(UInt32ArraySample(sample.category_hash));
+ m_prop_hash.set(UInt32ArraySample(sample.hash));
+ m_prop_type.set(Int32ArraySample(sample.type));
+ m_prop_color.set(C3fArraySample(sample.color));
+ m_prop_v1.set(V3fArraySample(sample.v1));
+ m_prop_v2.set(V3fArraySample(sample.v2));
+}
+
+/* ========================================================================= */
+
+AbcSimDebugReader::AbcSimDebugReader(SimDebugData *data) :
+ m_data(data)
+{
+}
+
+AbcSimDebugReader::~AbcSimDebugReader()
+{
+}
+
+void AbcSimDebugReader::init_abc(IObject object)
+{
+ if (m_object)
+ return;
+ m_object = IObject(object, kWrapExisting);
+ ICompoundProperty props = m_object.getProperties();
+
+ m_prop_category_hash = IUInt32ArrayProperty(props, "category_hash");
+ m_prop_hash = IUInt32ArrayProperty(props, "hash");
+ m_prop_type = IInt32ArrayProperty(props, "type");
+ m_prop_color = IC3fArrayProperty(props, "color");
+ m_prop_v1 = IV3fArrayProperty(props, "v1");
+ m_prop_v2 = IV3fArrayProperty(props, "v2");
+}
+
+static PTCReadSampleResult apply_sample(SimDebugData *data,
+ UInt32ArraySamplePtr sample_category_hash, UInt32ArraySamplePtr sample_hash,
+ Int32ArraySamplePtr sample_type, C3fArraySamplePtr sample_color,
+ V3fArraySamplePtr sample_v1, V3fArraySamplePtr sample_v2)
+{
+ int numelem = sample_hash->size();
+
+ if (sample_category_hash->size() != numelem ||
+ sample_type->size() != numelem ||
+ sample_color->size() != numelem ||
+ sample_v1->size() != numelem ||
+ sample_v2->size() != numelem)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ const uint32_t *data_category_hash = sample_category_hash->get();
+ const uint32_t *data_hash = sample_hash->get();
+ const int32_t *data_type = sample_type->get();
+ const C3f *data_color = sample_color->get();
+ const V3f *data_v1 = sample_v1->get();
+ const V3f *data_v2 = sample_v2->get();
+
+ for (int i = 0; i < numelem; ++i) {
+ BKE_sim_debug_data_add_element_ex(data, *data_type, data_v1->getValue(), data_v2->getValue(), data_color->x, data_color->y, data_color->z, *data_category_hash, *data_hash);
+
+ ++data_category_hash;
+ ++data_hash;
+ ++data_type;
+ ++data_color;
+ ++data_v1;
+ ++data_v2;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+PTCReadSampleResult AbcSimDebugReader::read_sample_abc(chrono_t time)
+{
+ if (!m_object)
+ return PTC_READ_SAMPLE_INVALID;
+
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ apply_sample(m_data, m_prop_category_hash.getValue(ss), m_prop_hash.getValue(ss), m_prop_type.getValue(ss),
+ m_prop_color.getValue(ss), m_prop_v1.getValue(ss), m_prop_v2.getValue(ss));
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_simdebug.h b/source/blender/pointcache/alembic/abc_simdebug.h
new file mode 100644
index 00000000000..b549a31cf34
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_simdebug.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_SIMDEBUG_H
+#define PTC_ABC_SIMDEBUG_H
+
+#include <Alembic/Abc/IObject.h>
+#include <Alembic/Abc/OObject.h>
+#include <Alembic/AbcGeom/Foundation.h>
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_writer.h"
+
+extern "C" {
+#include "BKE_effect.h"
+}
+
+namespace PTC {
+
+class AbcSimDebugWriter : public AbcWriter {
+public:
+ AbcSimDebugWriter(const std::string &name, SimDebugData *data);
+ ~AbcSimDebugWriter();
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ std::string m_name;
+ SimDebugData *m_data;
+
+ Abc::OObject m_object;
+ AbcGeom::OUInt32ArrayProperty m_prop_category_hash;
+ AbcGeom::OUInt32ArrayProperty m_prop_hash;
+ AbcGeom::OInt32ArrayProperty m_prop_type;
+ AbcGeom::OC3fArrayProperty m_prop_color;
+ AbcGeom::OV3fArrayProperty m_prop_v1;
+ AbcGeom::OV3fArrayProperty m_prop_v2;
+};
+
+class AbcSimDebugReader : public AbcReader {
+public:
+ AbcSimDebugReader(SimDebugData *data);
+ ~AbcSimDebugReader();
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ SimDebugData *m_data;
+
+ Abc::IObject m_object;
+ AbcGeom::IUInt32ArrayProperty m_prop_category_hash;
+ AbcGeom::IUInt32ArrayProperty m_prop_hash;
+ AbcGeom::IInt32ArrayProperty m_prop_type;
+ AbcGeom::IC3fArrayProperty m_prop_color;
+ AbcGeom::IV3fArrayProperty m_prop_v1;
+ AbcGeom::IV3fArrayProperty m_prop_v2;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_SIMDEBUG_H */
diff --git a/source/blender/pointcache/alembic/abc_split.cpp b/source/blender/pointcache/alembic/abc_split.cpp
new file mode 100644
index 00000000000..7a522a3f3c0
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_split.cpp
@@ -0,0 +1,236 @@
+//-*****************************************************************************
+//
+// Copyright (c) 2009-2013,
+// Sony Pictures Imageworks, Inc. and
+// Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Sony Pictures Imageworks, nor
+// Industrial Light & Magic nor the names of their contributors may be used
+// to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+//-*****************************************************************************
+
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include <map>
+
+#include <Alembic/AbcGeom/All.h>
+#include <Alembic/AbcCoreAbstract/All.h>
+#include <Alembic/AbcCoreFactory/All.h>
+#include <Alembic/Util/All.h>
+#include <Alembic/Abc/TypedPropertyTraits.h>
+
+#include "alembic.h"
+
+extern "C" {
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_cache_library.h"
+}
+
+using namespace ::Alembic::AbcGeom;
+
+namespace PTC {
+
+static void slice_properties(ICompoundProperty iParent, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter);
+
+static void slice_array_property(IArrayProperty iProp, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ OArrayProperty out(out_parent, iProp.getName(), iProp.getDataType(), iProp.getMetaData(), time_sampling);
+
+ ArrayPropertyReaderPtr reader = iProp.getPtr();
+ ArrayPropertyWriterPtr writer = out.getPtr();
+
+ size_t num_samples = iProp.getNumSamples();
+ if (num_samples == 0)
+ return;
+
+// index_t istart = reader->getFloorIndex(start).first;
+// index_t iend = reader->getFloorIndex(end).first;
+
+ char *buf = NULL;
+
+ for (index_t index = 0; index < iProp.getNumSamples(); ++index) {
+ chrono_t time = time_sampling->getSampleTime(index);
+ if (filter.use_time(time) || writer->getNumSamples() == 0) {
+ ArraySamplePtr sample_ptr;
+ reader->getSample(index, sample_ptr);
+
+ writer->setSample(*sample_ptr);
+ }
+ else {
+ writer->setFromPreviousSample();
+ }
+ }
+
+ if (buf)
+ delete[] buf;
+}
+
+static void slice_scalar_property(IScalarProperty iProp, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ OScalarProperty out(out_parent, iProp.getName(), iProp.getDataType(), iProp.getMetaData(), time_sampling);
+
+ ScalarPropertyReaderPtr reader = iProp.getPtr();
+ ScalarPropertyWriterPtr writer = out.getPtr();
+ size_t num_bytes = reader->getDataType().getNumBytes();
+
+ size_t num_samples = iProp.getNumSamples();
+ if (num_samples == 0)
+ return;
+
+// index_t istart = reader->getFloorIndex(start).first;
+// index_t iend = reader->getFloorIndex(end).first;
+
+ char *buf = new char[num_bytes];
+
+#if 0
+ if (istart > ostart) {
+ /* fill the gap between start indices with the first sample,
+ * so that output sample times match input sample times as close as possible.
+ */
+ for (index_t index = istart)
+ }
+#endif
+
+ for (index_t index = 0; index < iProp.getNumSamples(); ++index) {
+ chrono_t time = time_sampling->getSampleTime(index);
+ if (filter.use_time(time) || writer->getNumSamples() == 0) {
+ reader->getSample(index, (void*)buf);
+
+ writer->setSample((void*)buf);
+ }
+ else {
+ writer->setFromPreviousSample();
+ }
+ }
+
+ delete[] buf;
+}
+
+static void slice_compound_property(ICompoundProperty iProp, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ OCompoundProperty out(out_parent, iProp.getName(), iProp.getMetaData());
+
+ slice_properties(iProp, out, time_sampling, filter);
+}
+
+static void slice_properties(ICompoundProperty iParent, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) {
+ PropertyHeader header = iParent.getPropertyHeader(i);
+
+ if (header.isCompound()) {
+ slice_compound_property(ICompoundProperty(iParent, header.getName()), out_parent, time_sampling, filter);
+ }
+ else if (header.isScalar()) {
+ slice_scalar_property(IScalarProperty(iParent, header.getName()), out_parent, time_sampling, filter);
+ }
+ else {
+ BLI_assert(header.isArray());
+ slice_array_property(IArrayProperty(iParent, header.getName()), out_parent, time_sampling, filter);
+ }
+ }
+}
+
+typedef std::map<ObjectReaderPtr, ObjectWriterPtr> ObjectMap;
+typedef std::pair<ObjectReaderPtr, ObjectWriterPtr> ObjectPair;
+
+static void slice_object(IObject iObj, OObject out, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter, ObjectMap &object_map)
+{
+ // Get the properties.
+ slice_properties(iObj.getProperties(), out.getProperties(), time_sampling, filter);
+
+ // now the child objects
+ for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) {
+ const ObjectHeader &child_header = iObj.getChildHeader(i);
+ IObject child = IObject(iObj, child_header.getName());
+
+ /* Note: child instances are added later, once all actual objects have been copied */
+ if (!child.isInstanceRoot()) {
+ /* XXX reuse if the output object already exists.
+ * This should not happen, but currently some root objects are created
+ * in advance when opening writer archives. In the future these will not be needed
+ * and this check will become unnecessary.
+ */
+ OObject out_child = out.getChild(child_header.getName());
+ if (!out_child)
+ out_child = OObject(out, child_header.getName(), child_header.getMetaData());
+ object_map[child.getPtr()] = out_child.getPtr();
+
+ slice_object(child, out_child, time_sampling, filter, object_map);
+ }
+ }
+}
+
+static void slice_object_instances(IObject iObj, OObject out, const ObjectMap &object_map)
+{
+ // now the child objects
+ for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) {
+ const ObjectHeader &child_header = iObj.getChildHeader(i);
+ IObject child = IObject(iObj, child_header.getName());
+
+ if (child.isInstanceRoot()) {
+ ObjectMap::const_iterator it = object_map.find(child.getPtr());
+ BLI_assert(it != object_map.end());
+ OObject out_target(it->second, kWrapExisting);
+
+ out.addChildInstance(out_target, child_header.getName());
+ }
+ else {
+ OObject out_child(out.getChild(child_header.getName()).getPtr(), kWrapExisting);
+ slice_object_instances(child, out_child, object_map);
+ }
+ }
+}
+
+void abc_archive_slice(IArchive in, OArchive out, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ ObjectMap object_map;
+
+ slice_object(in.getTop(), out.getTop(), time_sampling, filter, object_map);
+ slice_object_instances(in.getTop(), out.getTop(), object_map);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_writer.cpp b/source/blender/pointcache/alembic/abc_writer.cpp
new file mode 100644
index 00000000000..a294858f2e5
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_writer.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+//#include <Alembic/AbcCoreHDF5/ReadWrite.h>
+#include <Alembic/AbcCoreOgawa/ReadWrite.h>
+#include <Alembic/Abc/OObject.h>
+#include <Alembic/Abc/ArchiveInfo.h>
+
+#include "alembic.h"
+#include "abc_writer.h"
+
+#include "util_error_handler.h"
+
+extern "C" {
+#include <ctime>
+
+#include "BLI_fileops.h"
+#include "BLI_path_util.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+
+/* make sure the file's directory exists */
+static void ensure_directory(const char *filename)
+{
+ char dir[FILE_MAXDIR];
+ BLI_split_dir_part(filename, dir, sizeof(dir));
+ BLI_dir_create_recursive(dir);
+}
+
+AbcWriterArchive *AbcWriterArchive::open(double fps, float start_frame, const std::string &filename, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, IDProperty *metadata, ErrorHandler *error_handler)
+{
+ ensure_directory(filename.c_str());
+
+ MetaData md = abc_create_archive_info(app_name, description, time, metadata);
+
+ OArchive abc_archive;
+ PTC_SAFE_CALL_BEGIN
+// abc_archive = OArchive(AbcCoreHDF5::WriteArchive(), filename, md, Abc::ErrorHandler::kThrowPolicy);
+ abc_archive = OArchive(AbcCoreOgawa::WriteArchive(), filename, md, Abc::ErrorHandler::kThrowPolicy);
+ PTC_SAFE_CALL_END_HANDLER(error_handler)
+
+ if (abc_archive)
+ return new AbcWriterArchive(fps, start_frame, resolutions, error_handler, abc_archive);
+ else
+ return NULL;
+}
+
+AbcWriterArchive::AbcWriterArchive(double fps, float start_frame, PTCArchiveResolution resolutions, ErrorHandler *error_handler, OArchive abc_archive) :
+ FrameMapper(fps, start_frame),
+ m_error_handler(error_handler),
+ m_use_render(false),
+ m_abc_archive(abc_archive)
+{
+ if (m_abc_archive) {
+ chrono_t cycle_time = this->seconds_per_frame();
+ chrono_t start_time = this->start_time();
+ m_frame_sampling = m_abc_archive.addTimeSampling(TimeSampling(cycle_time, start_time));
+
+ if (resolutions & PTC_RESOLUTION_PREVIEW)
+ m_abc_root = OObject(m_abc_archive.getTop(), "root");
+ if (resolutions & PTC_RESOLUTION_RENDER)
+ m_abc_root_render = OObject(m_abc_archive.getTop(), "root_render");
+ }
+}
+
+AbcWriterArchive::~AbcWriterArchive()
+{
+}
+
+OObject AbcWriterArchive::get_id_object(ID *id)
+{
+ if (!m_abc_archive)
+ return OObject();
+
+ ObjectWriterPtr root_ptr = root().getPtr();
+
+ ObjectWriterPtr child = root_ptr->getChild(id->name);
+ if (child)
+ return OObject(child, kWrapExisting);
+ else {
+ const ObjectHeader *child_header = root_ptr->getChildHeader(id->name);
+ if (child_header)
+ return OObject(root_ptr->createChild(*child_header), kWrapExisting);
+ else {
+ return OObject();
+ }
+ }
+}
+
+OObject AbcWriterArchive::root()
+{
+ if (m_use_render)
+ return m_abc_root_render ? m_abc_root_render : OObject();
+ else
+ return m_abc_root ? m_abc_root : OObject();
+}
+
+bool AbcWriterArchive::has_id_object(ID *id)
+{
+ if (!m_abc_archive)
+ return false;
+
+ ObjectWriterPtr root_ptr = root().getPtr();
+
+ return root_ptr->getChildHeader(id->name) != NULL;
+}
+
+TimeSamplingPtr AbcWriterArchive::frame_sampling()
+{
+ return m_abc_archive.getTimeSampling(m_frame_sampling);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_writer.h b/source/blender/pointcache/alembic/abc_writer.h
new file mode 100644
index 00000000000..60a25ff1e0a
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_writer.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ABC_WRITER_H
+#define PTC_ABC_WRITER_H
+
+#include <string>
+
+#include <Alembic/Abc/OArchive.h>
+#include <Alembic/Abc/OObject.h>
+
+#include "writer.h"
+
+#include "abc_frame_mapper.h"
+
+#include "util_error_handler.h"
+#include "util_types.h"
+
+extern "C" {
+#include "BLI_utildefines.h"
+
+#include "DNA_ID.h"
+}
+
+struct tm;
+
+namespace PTC {
+
+using namespace Alembic;
+
+class AbcWriterArchive : public WriterArchive, public FrameMapper {
+public:
+ virtual ~AbcWriterArchive();
+
+ static AbcWriterArchive *open(double fps, float start_frame, const std::string &filename, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, IDProperty *metadata, ErrorHandler *error_handler);
+
+ bool use_render() const { return m_use_render; }
+ void use_render(bool enable) { m_use_render = enable; }
+
+ Abc::OArchive abc_archive() const { return m_abc_archive; }
+ Abc::OObject root();
+
+ Abc::OObject get_id_object(ID *id);
+ bool has_id_object(ID *id);
+
+ template <class OObjectT>
+ OObjectT add_id_object(ID *id);
+
+ uint32_t frame_sampling_index() const { return m_frame_sampling; }
+ Abc::TimeSamplingPtr frame_sampling();
+
+protected:
+ AbcWriterArchive(double fps, float start_frame, PTCArchiveResolution resolutions, ErrorHandler *error_handler, Abc::OArchive abc_archive);
+
+protected:
+ ErrorHandler *m_error_handler;
+ uint32_t m_frame_sampling;
+ bool m_use_render;
+
+ Abc::OArchive m_abc_archive;
+ Abc::OObject m_abc_root;
+ Abc::OObject m_abc_root_render;
+};
+
+class AbcWriter : public Writer {
+public:
+ Abc::TimeSamplingPtr frame_sampling() { return m_abc_archive->frame_sampling(); }
+
+ void init(WriterArchive *archive)
+ {
+ BLI_assert(dynamic_cast<AbcWriterArchive*>(archive));
+ m_abc_archive = static_cast<AbcWriterArchive*>(archive);
+
+ init_abc();
+ }
+
+ /* one of these should be implemented by subclasses */
+ virtual void init_abc() {}
+ virtual void init_abc(Abc::OObject /*parent*/) {}
+
+ AbcWriterArchive *abc_archive() const { return m_abc_archive; }
+
+private:
+ AbcWriterArchive *m_abc_archive;
+};
+
+/* ------------------------------------------------------------------------- */
+
+template <class OObjectT>
+OObjectT AbcWriterArchive::add_id_object(ID *id)
+{
+ using namespace Abc;
+
+ if (!m_abc_archive)
+ return OObjectT();
+
+ ObjectWriterPtr root_ptr = this->root().getPtr();
+
+ ObjectWriterPtr child = root_ptr->getChild(id->name);
+ if (child)
+ return OObjectT(child, kWrapExisting);
+ else {
+ const ObjectHeader *child_header = root_ptr->getChildHeader(id->name);
+ if (child_header)
+ return OObjectT(root_ptr->createChild(*child_header), kWrapExisting);
+ else {
+ return OObjectT(root_ptr, id->name, frame_sampling_index());
+ }
+ }
+}
+
+} /* namespace PTC */
+
+#endif /* PTC_WRITER_H */
diff --git a/source/blender/pointcache/alembic/alembic.cpp b/source/blender/pointcache/alembic/alembic.cpp
new file mode 100644
index 00000000000..d0985167908
--- /dev/null
+++ b/source/blender/pointcache/alembic/alembic.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "PTC_api.h"
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_writer.h"
+#include "abc_cloth.h"
+#include "abc_group.h"
+#include "abc_mesh.h"
+#include "abc_object.h"
+#include "abc_particles.h"
+
+#include "alembic.h"
+
+namespace PTC {
+
+struct ArchiveSlicesFilter : public AbcArchiveFrameFilter {
+ std::vector<Abc::chrono_t> start;
+ std::vector<Abc::chrono_t> end;
+
+ bool use_time(Abc::chrono_t time) const
+ {
+ assert(start.size() == end.size());
+ for (size_t i = 0; i < start.size(); ++i) {
+ if (time >= start[i] && time <= end[i])
+ return true;
+ }
+ return false;
+ }
+};
+
+class AbcFactory : public Factory {
+ const std::string &get_default_extension()
+ {
+ static std::string ext = "abc";
+ return ext;
+ }
+
+ WriterArchive *open_writer_archive(double fps, float start_frame, const std::string &name, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata, ErrorHandler *error_handler)
+ {
+ return AbcWriterArchive::open(fps, start_frame, name, resolutions, app_name, description, time, metadata, error_handler);
+ }
+
+ ReaderArchive *open_reader_archive(double fps, float start_frame, const std::string &name, ErrorHandler *error_handler)
+ {
+ return AbcReaderArchive::open(fps, start_frame, name, error_handler);
+ }
+
+ void slice(ReaderArchive *in, WriterArchive *out, struct ListBase *slices)
+ {
+ BLI_assert(dynamic_cast<AbcReaderArchive*>(in));
+ BLI_assert(dynamic_cast<AbcWriterArchive*>(out));
+ AbcReaderArchive *abc_in = static_cast<AbcReaderArchive*>(in);
+ AbcWriterArchive *abc_out = static_cast<AbcWriterArchive*>(out);
+
+ ArchiveSlicesFilter abc_filter;
+ for (CacheSlice *slice = (CacheSlice *)slices->first; slice; slice = slice->next) {
+ abc_filter.start.push_back(abc_in->frame_to_time(slice->start));
+ abc_filter.end.push_back(abc_in->frame_to_time(slice->end));
+ }
+
+ abc_archive_slice(abc_in->abc_archive(), abc_out->abc_archive(), abc_out->frame_sampling(), abc_filter);
+ }
+
+ Writer *create_writer_object(const std::string &name, Scene *scene, Object *ob)
+ {
+ return new AbcObjectWriter(name, scene, ob, true, true);
+ }
+
+ Reader *create_reader_object(const std::string &name, Object *ob)
+ {
+ return new AbcObjectReader(name, ob);
+ }
+
+ Writer *create_writer_group(const std::string &name, Group *group)
+ {
+ return new AbcGroupWriter(name, group);
+ }
+
+ Reader *create_reader_group(const std::string &name, Group *group)
+ {
+ return new AbcGroupReader(name, group);
+ }
+
+
+ /* Cloth */
+ Writer *create_writer_cloth(const std::string &name, Object *ob, ClothModifierData *clmd)
+ {
+ return new AbcClothWriter(name, ob, clmd);
+ }
+
+ Reader *create_reader_cloth(const std::string &name, Object *ob, ClothModifierData *clmd)
+ {
+ return new AbcClothReader(name, ob, clmd);
+ }
+
+ /* Modifier Stack */
+ Writer *create_writer_derived_mesh(const std::string &name, Object *ob, DerivedMesh **dm_ptr)
+ {
+ return new AbcDerivedMeshWriter(name, ob, dm_ptr);
+ }
+
+ Reader *create_reader_derived_mesh(const std::string &name, Object *ob)
+ {
+ return new AbcDerivedMeshReader(name, ob);
+ }
+
+ Writer *create_writer_derived_final_realtime(const std::string &name, Object *ob)
+ {
+ return new AbcDerivedFinalRealtimeWriter(name, ob);
+ }
+
+ Writer *create_writer_derived_final_render(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr)
+ {
+ return new AbcDerivedFinalRenderWriter(name, scene, ob, render_dm_ptr);
+ }
+
+
+ Writer *create_writer_dupligroup(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib)
+ {
+ return new AbcDupligroupWriter(name, eval_ctx, scene, group, cachelib);
+ }
+
+ Writer *create_writer_duplicache(const std::string &name, Group *group, DupliCache *dupcache, int datatypes, bool do_sim_debug)
+ {
+ return new AbcDupliCacheWriter(name, group, dupcache, datatypes, do_sim_debug);
+ }
+
+ Reader *create_reader_duplicache(const std::string &name, Group *group, DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug)
+ {
+ return new AbcDupliCacheReader(name, group, dupcache, read_strands_motion, read_strands_children, read_sim_debug);
+ }
+
+ Reader *create_reader_duplicache_object(const std::string &name, Object *ob, DupliObjectData *data,
+ bool read_strands_motion, bool read_strands_children)
+ {
+ return new AbcDupliObjectReader(name, ob, data, read_strands_motion, read_strands_children);
+ }
+};
+
+}
+
+void PTC_alembic_init()
+{
+ static PTC::AbcFactory abc_factory;
+
+ PTC::Factory::alembic = &abc_factory;
+}
diff --git a/source/blender/pointcache/alembic/alembic.h b/source/blender/pointcache/alembic/alembic.h
new file mode 100644
index 00000000000..fc387edaa88
--- /dev/null
+++ b/source/blender/pointcache/alembic/alembic.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_ALEMBIC_H
+#define PTC_ALEMBIC_H
+
+#include <string>
+
+#include <Alembic/Abc/IArchive.h>
+
+struct CacheArchiveInfo;
+struct IDProperty;
+
+namespace PTC {
+
+using namespace Alembic;
+
+void abc_metadata_from_idprops_group(Abc::MetaData &md, IDProperty *prop);
+void abc_metadata_to_idprops_group(const Abc::MetaData &md, IDProperty *prop);
+Abc::MetaData abc_create_archive_info(const char *app_name, const char *description, const struct tm *t, struct IDProperty *props);
+
+void abc_archive_info_stream(Alembic::Abc::IArchive &archive, void (*stream)(void *, const char *), void *userdata);
+void abc_archive_info_nodes(Alembic::Abc::IArchive &archive, CacheArchiveInfo *info, IDProperty *metadata, bool calc_nodes, bool calc_bytes_size);
+
+struct AbcArchiveFrameFilter {
+ virtual bool use_time(Abc::chrono_t time) const = 0;
+};
+
+void abc_archive_slice(Abc::IArchive in, Abc::OArchive out, Abc::TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter);
+
+} /* namespace PTC */
+
+#endif /* PTC_CLOTH_H */
diff --git a/source/blender/pointcache/intern/ptc_types.cpp b/source/blender/pointcache/intern/ptc_types.cpp
new file mode 100644
index 00000000000..f2df356ca5b
--- /dev/null
+++ b/source/blender/pointcache/intern/ptc_types.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "ptc_types.h"
+
+extern "C" {
+#include "BKE_DerivedMesh.h"
+}
+
+namespace PTC {
+
+Factory *Factory::alembic = NULL;
+
+DerivedMesh *DerivedMeshReader::acquire_result()
+{
+ DerivedMesh *dm = m_result;
+ m_result = NULL;
+ return dm;
+}
+
+void DerivedMeshReader::discard_result()
+{
+ if (m_result) {
+ m_result->release(m_result);
+ m_result = NULL;
+ }
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/intern/ptc_types.h b/source/blender/pointcache/intern/ptc_types.h
new file mode 100644
index 00000000000..091afa1f7b5
--- /dev/null
+++ b/source/blender/pointcache/intern/ptc_types.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_TYPES_H
+#define PTC_TYPES_H
+
+#include "reader.h"
+#include "writer.h"
+
+extern "C" {
+#include "DNA_group_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_object_force.h"
+#include "DNA_particle_types.h"
+}
+
+struct CacheLibrary;
+struct IDProperty;
+
+namespace PTC {
+
+class ClothWriter {
+public:
+ ClothWriter(Object *ob, ClothModifierData *clmd, const std::string &name) :
+ m_ob(ob),
+ m_clmd(clmd),
+ m_name(name)
+ {}
+
+ ~ClothWriter()
+ {}
+
+protected:
+ Object *m_ob;
+ ClothModifierData *m_clmd;
+ std::string m_name;
+};
+
+class ClothReader {
+public:
+ ClothReader(Object *ob, ClothModifierData *clmd, const std::string &name) :
+ m_ob(ob),
+ m_clmd(clmd),
+ m_name(name)
+ {}
+
+ ~ClothReader()
+ {}
+
+protected:
+ Object *m_ob;
+ ClothModifierData *m_clmd;
+ std::string m_name;
+};
+
+
+class DerivedMeshWriter {
+public:
+ /** \note Targeted DerivedMesh at \a dm_ptr must be available only on \fn write_sample calls */
+ DerivedMeshWriter(Object *ob, DerivedMesh **dm_ptr, const std::string &name) :
+ m_ob(ob),
+ m_dm_ptr(dm_ptr),
+ m_name(name)
+ {}
+
+ ~DerivedMeshWriter()
+ {}
+
+protected:
+ Object *m_ob;
+ DerivedMesh **m_dm_ptr;
+ std::string m_name;
+};
+
+class DerivedMeshReader {
+public:
+ DerivedMeshReader(Object *ob, const std::string &name) :
+ m_ob(ob),
+ m_result(0),
+ m_name(name)
+ {}
+
+ ~DerivedMeshReader()
+ {
+ discard_result();
+ }
+
+ DerivedMesh *acquire_result();
+ void discard_result();
+
+protected:
+ Object *m_ob;
+ DerivedMesh *m_result;
+ std::string m_name;
+};
+
+class GroupWriter {
+public:
+ GroupWriter(Group *group, const std::string &name) :
+ m_group(group),
+ m_name(name)
+ {}
+
+protected:
+ Group *m_group;
+ std::string m_name;
+};
+
+class GroupReader {
+public:
+ GroupReader(Group *group, const std::string &name) :
+ m_group(group),
+ m_name(name)
+ {}
+
+protected:
+ Group *m_group;
+ std::string m_name;
+};
+
+class ObjectWriter {
+public:
+ ObjectWriter(Object *ob, const std::string &name) :
+ m_ob(ob),
+ m_name(name)
+ {}
+
+protected:
+ Object *m_ob;
+ std::string m_name;
+};
+
+class ObjectReader {
+public:
+ ObjectReader(Object *ob, const std::string &name) :
+ m_ob(ob),
+ m_name(name)
+ {}
+
+protected:
+ Object *m_ob;
+ std::string m_name;
+};
+
+class ParticlesWriter {
+public:
+ ParticlesWriter(Object *ob, ParticleSystem *psys, const std::string &name) :
+ m_ob(ob),
+ m_psys(psys),
+ m_name(name)
+ {}
+
+ ~ParticlesWriter()
+ {}
+
+protected:
+ Object *m_ob;
+ ParticleSystem *m_psys;
+ std::string m_name;
+};
+
+class ParticlesReader {
+public:
+ ParticlesReader(Object *ob, ParticleSystem *psys, const std::string &name) :
+ m_ob(ob),
+ m_psys(psys),
+ m_name(name),
+ m_totpoint(0)
+ {}
+
+ ~ParticlesReader()
+ {}
+
+ int totpoint() const { return m_totpoint; }
+
+protected:
+ Object *m_ob;
+ ParticleSystem *m_psys;
+ std::string m_name;
+
+ int m_totpoint;
+};
+
+struct Factory {
+ virtual const std::string &get_default_extension() = 0;
+ virtual WriterArchive *open_writer_archive(double fps, float start_frame, const std::string &name, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata, ErrorHandler *error_handler) = 0;
+ virtual ReaderArchive *open_reader_archive(double fps, float start_frame, const std::string &name, ErrorHandler *error_handler) = 0;
+
+ virtual void slice(ReaderArchive *in, WriterArchive *out, struct ListBase *slices) = 0;
+
+ virtual Writer *create_writer_object(const std::string &name, Scene *scene, Object *ob) = 0;
+ virtual Reader *create_reader_object(const std::string &name, Object *ob) = 0;
+
+ virtual Writer *create_writer_group(const std::string &name, Group *group) = 0;
+ virtual Reader *create_reader_group(const std::string &name, Group *group) = 0;
+
+ /* Cloth */
+ virtual Writer *create_writer_cloth(const std::string &name, Object *ob, ClothModifierData *clmd) = 0;
+ virtual Reader *create_reader_cloth(const std::string &name, Object *ob, ClothModifierData *clmd) = 0;
+
+ /* Modifier Stack */
+ virtual Writer *create_writer_derived_mesh(const std::string &name, Object *ob, DerivedMesh **dm_ptr) = 0;
+ virtual Reader *create_reader_derived_mesh(const std::string &name, Object *ob) = 0;
+
+ virtual Writer *create_writer_derived_final_realtime(const std::string &name, Object *ob) = 0;
+ virtual Writer *create_writer_derived_final_render(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr) = 0;
+
+ virtual Writer *create_writer_dupligroup(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib) = 0;
+ virtual Writer *create_writer_duplicache(const std::string &name, Group *group, DupliCache *dupcache, int datatypes, bool do_sim_debug) = 0;
+ virtual Reader *create_reader_duplicache(const std::string &name, Group *group, DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug) = 0;
+ virtual Reader *create_reader_duplicache_object(const std::string &name, Object *ob, DupliObjectData *data,
+ bool read_strands_motion, bool read_strands_children) = 0;
+
+ static Factory *alembic;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_EXPORT_H */
diff --git a/source/blender/pointcache/intern/reader.cpp b/source/blender/pointcache/intern/reader.cpp
new file mode 100644
index 00000000000..56b0f54e982
--- /dev/null
+++ b/source/blender/pointcache/intern/reader.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "reader.h"
+#include "util_error_handler.h"
+
+extern "C" {
+#include "DNA_scene_types.h"
+}
+
+namespace PTC {
+
+Reader::Reader() :
+ m_error_handler(0)
+{
+}
+
+Reader::Reader(ErrorHandler *error_handler) :
+ m_error_handler(error_handler)
+{
+}
+
+Reader::~Reader()
+{
+ if (m_error_handler)
+ delete m_error_handler;
+}
+
+void Reader::set_error_handler(ErrorHandler *handler)
+{
+ if (m_error_handler)
+ delete m_error_handler;
+
+ m_error_handler = handler;
+}
+
+bool Reader::valid() const
+{
+ return m_error_handler ? m_error_handler->max_error_level() >= PTC_ERROR_CRITICAL : true;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/intern/reader.h b/source/blender/pointcache/intern/reader.h
new file mode 100644
index 00000000000..39d0ab20197
--- /dev/null
+++ b/source/blender/pointcache/intern/reader.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_READER_H
+#define PTC_READER_H
+
+#include <string>
+
+#include "util_error_handler.h"
+#include "util_types.h"
+#include "PTC_api.h"
+
+struct ID;
+struct IDProperty;
+struct CacheArchiveInfo;
+
+namespace PTC {
+
+class ReaderArchive {
+public:
+ virtual ~ReaderArchive() {}
+
+ virtual PTCArchiveResolution get_resolutions() = 0;
+ virtual void use_render(bool enable) = 0;
+
+ virtual bool get_frame_range(int &start_frame, int &end_frame) = 0;
+ virtual void get_info_stream(void (*stream)(void *, const char *), void *userdata) = 0;
+ virtual void get_info(CacheArchiveInfo *info, IDProperty *metadata) = 0;
+ virtual void get_info_nodes(CacheArchiveInfo *info, bool calc_bytes_size) = 0;
+};
+
+class Reader {
+public:
+ Reader();
+ Reader(ErrorHandler *error_handler);
+ virtual ~Reader();
+
+ virtual void init(ReaderArchive *archive) = 0;
+
+ void set_error_handler(ErrorHandler *handler);
+ ErrorHandler *get_error_handler() const { return m_error_handler; }
+ bool valid() const;
+
+ virtual bool get_frame_range(int &start_frame, int &end_frame) = 0;
+ virtual PTCReadSampleResult test_sample(float frame) = 0;
+ virtual PTCReadSampleResult read_sample(float frame) = 0;
+
+protected:
+ ErrorHandler *m_error_handler;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_READER_H */
diff --git a/source/blender/pointcache/intern/writer.cpp b/source/blender/pointcache/intern/writer.cpp
new file mode 100644
index 00000000000..97a6b9ad405
--- /dev/null
+++ b/source/blender/pointcache/intern/writer.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "writer.h"
+
+extern "C" {
+#include "DNA_scene_types.h"
+}
+
+namespace PTC {
+
+Writer::Writer() :
+ m_error_handler(0)
+{
+}
+
+Writer::Writer(ErrorHandler *handler) :
+ m_error_handler(handler)
+{
+}
+
+Writer::~Writer()
+{
+ if (m_error_handler)
+ delete m_error_handler;
+}
+
+void Writer::set_error_handler(ErrorHandler *handler)
+{
+ if (m_error_handler)
+ delete m_error_handler;
+
+ m_error_handler = handler;
+}
+
+bool Writer::valid() const
+{
+ return m_error_handler ? m_error_handler->max_error_level() >= PTC_ERROR_CRITICAL : true;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/intern/writer.h b/source/blender/pointcache/intern/writer.h
new file mode 100644
index 00000000000..008c391bffc
--- /dev/null
+++ b/source/blender/pointcache/intern/writer.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_WRITER_H
+#define PTC_WRITER_H
+
+#include <string>
+
+#include "util_error_handler.h"
+
+struct ID;
+
+namespace PTC {
+
+class WriterArchive {
+public:
+ virtual ~WriterArchive() {}
+
+ virtual void use_render(bool enable) = 0;
+};
+
+class Writer {
+public:
+ Writer();
+ Writer(ErrorHandler *handler);
+ virtual ~Writer();
+
+ void set_error_handler(ErrorHandler *handler);
+ bool valid() const;
+
+ virtual void init(WriterArchive *archive) = 0;
+
+ /* create references to other objects */
+ virtual void create_refs() {}
+
+ virtual void write_sample() = 0;
+
+protected:
+ ErrorHandler *m_error_handler;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_WRITER_H */
diff --git a/source/blender/pointcache/util/util_error_handler.cpp b/source/blender/pointcache/util/util_error_handler.cpp
new file mode 100644
index 00000000000..e1a326e1713
--- /dev/null
+++ b/source/blender/pointcache/util/util_error_handler.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include <iostream>
+
+#include "util_error_handler.h"
+
+extern "C" {
+#include "BKE_modifier.h"
+}
+
+namespace PTC {
+
+ErrorHandler *ErrorHandler::m_default_handler = new StdErrorHandler(PTC_ERROR_INFO);
+
+ErrorHandler::ErrorHandler() :
+ m_max_level(PTC_ERROR_NONE)
+{
+}
+
+ErrorHandler::~ErrorHandler()
+{
+}
+
+void ErrorHandler::set_error_level(PTCErrorLevel level)
+{
+ if (level > m_max_level)
+ m_max_level = level;
+}
+
+void ErrorHandler::set_default_handler(ErrorHandler *handler)
+{
+ if (m_default_handler)
+ delete m_default_handler;
+
+ if (handler)
+ m_default_handler = handler;
+ else
+ m_default_handler = new StdErrorHandler(PTC_ERROR_INFO);
+}
+
+void ErrorHandler::clear_default_handler()
+{
+ if (m_default_handler)
+ delete m_default_handler;
+
+ m_default_handler = new StdErrorHandler(PTC_ERROR_INFO);
+}
+
+
+StdErrorHandler::StdErrorHandler(PTCErrorLevel level) :
+ m_verbosity(level)
+{
+}
+
+void StdErrorHandler::handle(PTCErrorLevel level, const char *message)
+{
+ /* ignore levels below the verbosity setting */
+ if (level >= m_verbosity) {
+ std::cerr << message << std::endl;
+ }
+}
+
+
+CallbackErrorHandler::CallbackErrorHandler(PTCErrorCallback cb, void *userdata) :
+ m_callback(cb),
+ m_userdata(userdata)
+{
+}
+
+void CallbackErrorHandler::handle(PTCErrorLevel level, const char *message)
+{
+ m_callback(m_userdata, level, message);
+}
+
+
+ModifierErrorHandler::ModifierErrorHandler(ModifierData *md) :
+ m_modifier(md)
+{
+}
+
+void ModifierErrorHandler::handle(PTCErrorLevel UNUSED(level), const char *message)
+{
+ modifier_setError(m_modifier, "%s", message);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/util/util_error_handler.h b/source/blender/pointcache/util/util_error_handler.h
new file mode 100644
index 00000000000..d706890ea59
--- /dev/null
+++ b/source/blender/pointcache/util/util_error_handler.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_UTIL_ERROR_HANDLER_H
+#define PTC_UTIL_ERROR_HANDLER_H
+
+#include <stdio.h>
+
+#ifdef WITH_ALEMBIC
+#include <Alembic/Abc/ErrorHandler.h>
+#endif
+
+extern "C" {
+#include "BLI_utildefines.h"
+#include "BLI_string.h"
+}
+
+#include "util_types.h"
+
+struct ModifierData;
+struct ReportList;
+
+namespace PTC {
+
+class ErrorHandler
+{
+public:
+ ErrorHandler();
+ virtual ~ErrorHandler();
+
+ virtual void handle(PTCErrorLevel level, const char *message) = 0;
+ void set_error_level(PTCErrorLevel level);
+ PTCErrorLevel max_error_level() const { return m_max_level; }
+
+ static ErrorHandler *get_default_handler() { return m_default_handler; }
+ static void set_default_handler(ErrorHandler *handler);
+ static void clear_default_handler();
+
+private:
+ PTCErrorLevel m_max_level;
+
+ static ErrorHandler *m_default_handler;
+};
+
+
+class StdErrorHandler : public ErrorHandler
+{
+public:
+ StdErrorHandler(PTCErrorLevel level);
+
+ void handle(PTCErrorLevel level, const char *message);
+
+ PTCErrorLevel get_verbosity() const { return m_verbosity; }
+ void set_verbosity(PTCErrorLevel level) { m_verbosity = level; }
+
+private:
+ PTCErrorLevel m_verbosity;
+};
+
+
+/* Use Blender reports system to log Alembic errors */
+class CallbackErrorHandler : public ErrorHandler
+{
+public:
+ CallbackErrorHandler(PTCErrorCallback cb, void *userdata);
+
+ void handle(PTCErrorLevel level, const char *message);
+
+private:
+ PTCErrorCallback m_callback;
+ void *m_userdata;
+};
+
+
+class ModifierErrorHandler : public ErrorHandler
+{
+public:
+ ModifierErrorHandler(ModifierData *md);
+
+ void handle(PTCErrorLevel level, const char *message);
+
+private:
+ ModifierData *m_modifier;
+};
+
+/* -------------------------------- */
+
+#ifdef WITH_ALEMBIC
+
+/* XXX With current Alembic version 1.5 we only get a combined error message.
+ * This function try to extract some more information and return a nicer message format.
+ */
+BLI_INLINE void split_alembic_error_message(const char *msg, const char **origin, const char **base_msg)
+{
+ const char delim[] = {'\n', '\0'};
+ const char *sep, *suffix;
+
+ BLI_str_partition(msg, delim, &sep, &suffix);
+ if (suffix) {
+ *origin = msg;
+ BLI_str_partition(suffix, delim, &sep, &suffix);
+ if (suffix) {
+ *base_msg = suffix;
+ }
+ else {
+ *base_msg = msg;
+ }
+ }
+ else {
+ *origin = *base_msg = msg;
+ }
+}
+
+/* wrapper templates so the exception macro can be used with references as well as pointers */
+
+template <typename T>
+void handle_alembic_exception(T &handler, PTCErrorLevel level, const Alembic::Util::Exception &e)
+{
+ const char *origin, *msg;
+ split_alembic_error_message(e.what(), &origin, &msg);
+
+ handler.set_error_level(level);
+ handler.handle(level, msg);
+}
+
+template <typename T>
+void handle_alembic_exception(T *handler, PTCErrorLevel level, const Alembic::Util::Exception &e)
+{
+ static StdErrorHandler default_handler(PTC_ERROR_WARNING);
+ if (!handler)
+ handler = &default_handler;
+
+ const char *origin, *msg;
+ split_alembic_error_message(e.what(), &origin, &msg);
+
+ handler->set_error_level(level);
+ handler->handle(level, msg);
+}
+
+#endif
+
+/* -------------------------------- */
+
+/* macros for convenient exception handling */
+
+#define PTC_SAFE_CALL_BEGIN \
+ try {
+
+#ifdef WITH_ALEMBIC
+#define PTC_SAFE_CALL_END_HANDLER(handler) \
+ } \
+ catch (Alembic::Util::Exception e) { \
+ handle_alembic_exception((handler), PTC_ERROR_CRITICAL, e); \
+ }
+#else
+#define PTC_SAFE_CALL_END_HANDLER(handler) \
+ }
+#endif
+
+#ifdef WITH_ALEMBIC
+#define PTC_SAFE_CALL_END_HANDLER_LEVEL(handler, level) \
+ } \
+ catch (Alembic::Util::Exception e) { \
+ handle_alembic_exception((handler), (level), e); \
+ }
+#else
+#define PTC_SAFE_CALL_END_HANDLER_LEVEL(handler, level) \
+ }
+#endif
+
+#ifdef WITH_ALEMBIC
+#define PTC_SAFE_CALL_END \
+ } \
+ catch (Alembic::Util::Exception e) { \
+ handle_alembic_exception(ErrorHandler::get_default_handler(), PTC_ERROR_CRITICAL, e); \
+ }
+#else
+#define PTC_SAFE_CALL_END \
+ }
+#endif
+
+/* -------------------------------- */
+
+} /* namespace PTC */
+
+#endif /* PTC_UTIL_ERROR_HANDLER_H */
diff --git a/source/blender/pointcache/util/util_function.h b/source/blender/pointcache/util/util_function.h
new file mode 100644
index 00000000000..97f0278237d
--- /dev/null
+++ b/source/blender/pointcache/util/util_function.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_UTIL_FUNCTION
+#define PTC_UTIL_FUNCTION
+
+#if __cplusplus > 199711L
+
+#include <functional>
+
+namespace PTC {
+
+using std::function;
+using namespace std::placeholders;
+#define function_bind std::bind
+
+} /* namespace PTC */
+
+#else
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+namespace PTC {
+
+using boost::function;
+#define function_bind boost::bind
+
+} /* namespace PTC */
+
+#endif
+
+#endif /* PTC_UTIL_FUNCTION */
diff --git a/source/blender/pointcache/util/util_task.cpp b/source/blender/pointcache/util/util_task.cpp
new file mode 100644
index 00000000000..feecdd54b97
--- /dev/null
+++ b/source/blender/pointcache/util/util_task.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "util_task.h"
+
+#include "BLI_task.h"
+#include "BLI_threads.h"
+
+namespace PTC {
+
+class UtilTask {
+public:
+ explicit UtilTask(const UtilTaskFunction& run) : run_(run) {}
+ void run()
+ {
+ run_();
+ }
+protected:
+ UtilTaskFunction run_;
+};
+
+static void task_function(TaskPool * /*pool*/,
+ void *taskdata,
+ int /*threadid*/)
+{
+ UtilTask *task = reinterpret_cast<UtilTask*>(taskdata);
+ task->run();
+ delete task;
+}
+
+UtilTaskPool::UtilTaskPool()
+{
+ scheduler_ = BLI_task_scheduler_get();
+ pool_ = BLI_task_pool_create(scheduler_, NULL);
+}
+
+UtilTaskPool::~UtilTaskPool()
+{
+ BLI_task_pool_free(pool_);
+}
+
+void UtilTaskPool::push(const UtilTaskFunction& run, bool front)
+{
+ UtilTask *task = new UtilTask(run);
+ BLI_task_pool_push(pool_,
+ task_function,
+ task,
+ false,
+ front? TASK_PRIORITY_HIGH: TASK_PRIORITY_LOW);
+}
+
+void UtilTaskPool::wait_work()
+{
+ BLI_task_pool_work_and_wait(pool_);
+}
+
+void UtilTaskPool::cancel()
+{
+ BLI_task_pool_cancel(pool_);
+}
+
+void UtilTaskPool::stop()
+{
+ BLI_task_pool_stop(pool_);
+}
+
+bool UtilTaskPool::cancelled()
+{
+ return BLI_task_pool_canceled(pool_);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/util/util_task.h b/source/blender/pointcache/util/util_task.h
new file mode 100644
index 00000000000..be2f1cca84f
--- /dev/null
+++ b/source/blender/pointcache/util/util_task.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_UTIL_TASK_H
+#define PTC_UTIL_TASK_H
+
+#include "util_function.h"
+
+struct TaskScheduler;
+struct TaskPool;
+
+namespace PTC {
+
+typedef function<void(void)> UtilTaskFunction;
+
+class UtilTaskPool {
+public:
+ UtilTaskPool();
+ ~UtilTaskPool();
+
+ void push(const UtilTaskFunction& run, bool front = false);
+
+ void wait_work();
+ void cancel();
+ void stop();
+
+ bool cancelled();
+
+protected:
+ struct TaskScheduler *scheduler_;
+ struct TaskPool *pool_;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_UTIL_TASK_H */
diff --git a/source/blender/pointcache/util/util_thread.h b/source/blender/pointcache/util/util_thread.h
new file mode 100644
index 00000000000..6b41a35ee3f
--- /dev/null
+++ b/source/blender/pointcache/util/util_thread.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_UTIL_THREAD_H
+#define PTC_UTIL_THREAD_H
+
+#include "BLI_threads.h"
+
+namespace PTC {
+
+class thread_mutex {
+public:
+ thread_mutex()
+ {
+ BLI_mutex_init(&mutex_);
+ }
+
+ ~thread_mutex()
+ {
+ BLI_mutex_end(&mutex_);
+ }
+
+ void lock()
+ {
+ BLI_mutex_lock(&mutex_);
+ }
+
+ bool trylock()
+ {
+ return BLI_mutex_trylock(&mutex_);
+ }
+
+ void unlock()
+ {
+ BLI_mutex_unlock(&mutex_);
+ }
+
+protected:
+ ThreadMutex mutex_;
+};
+
+class thread_scoped_lock {
+public:
+ explicit thread_scoped_lock(thread_mutex& mutex)
+ : mutex_(mutex)
+ {
+ mutex_.lock();
+ }
+
+ ~thread_scoped_lock() {
+ mutex_.unlock();
+ }
+protected:
+ thread_mutex& mutex_;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_UTIL_THREAD_H */
diff --git a/source/blender/pointcache/util/util_types.h b/source/blender/pointcache/util/util_types.h
new file mode 100644
index 00000000000..0ee027c7700
--- /dev/null
+++ b/source/blender/pointcache/util/util_types.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef PTC_UTIL_TYPES_H
+#define PTC_UTIL_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum PTCArchiveResolution {
+ PTC_RESOLUTION_NONE = 0,
+ PTC_RESOLUTION_PREVIEW = (1 << 0),
+ PTC_RESOLUTION_RENDER = (1 << 1),
+} PTCArchiveResolution;
+
+typedef enum PTCErrorLevel {
+ PTC_ERROR_NONE = 0,
+ PTC_ERROR_INFO = 1,
+ PTC_ERROR_WARNING = 2,
+ PTC_ERROR_CRITICAL = 3,
+} PTCErrorLevel;
+
+typedef void (*PTCErrorCallback)(void *userdata, PTCErrorLevel level, const char *message);
+
+typedef enum PTCReadSampleResult {
+ PTC_READ_SAMPLE_INVALID = 0, /* no valid result can be retrieved */
+ PTC_READ_SAMPLE_EARLY, /* request time before first sample */
+ PTC_READ_SAMPLE_LATE, /* request time after last sample */
+ PTC_READ_SAMPLE_EXACT, /* found sample for requested frame */
+ PTC_READ_SAMPLE_INTERPOLATED /* no exact sample, but found enclosing samples for interpolation */
+} PTCReadSampleResult;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PTC_UTIL_TYPES_H */