diff options
Diffstat (limited to 'source/blender/pointcache')
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 */ |