2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
20 * ***** END GPL LICENSE BLOCK *****
23 #include "../ABC_alembic.h"
25 #include <Alembic/AbcMaterial/IMaterial.h>
27 #include "abc_archive.h"
28 #include "abc_camera.h"
29 #include "abc_curves.h"
32 #include "abc_nurbs.h"
33 #include "abc_points.h"
34 #include "abc_transform.h"
38 #include "MEM_guardedalloc.h"
40 #include "DNA_cachefile_types.h"
41 #include "DNA_curve_types.h"
42 #include "DNA_modifier_types.h"
43 #include "DNA_object_types.h"
44 #include "DNA_scene_types.h"
46 #include "BKE_cachefile.h"
47 #include "BKE_cdderivedmesh.h"
48 #include "BKE_context.h"
49 #include "BKE_curve.h"
50 #include "BKE_depsgraph.h"
51 #include "BKE_global.h"
52 #include "BKE_library.h"
54 #include "BKE_scene.h"
56 /* SpaceType struct has a member called 'new' which obviously conflicts with C++
57 * so temporarily redefining the new keyword to make it compile. */
58 #define new extern_new
59 #include "BKE_screen.h"
62 #include "BLI_fileops.h"
63 #include "BLI_ghash.h"
64 #include "BLI_listbase.h"
66 #include "BLI_path_util.h"
67 #include "BLI_string.h"
73 using Alembic::Abc::Int32ArraySamplePtr;
74 using Alembic::Abc::ObjectHeader;
76 using Alembic::AbcGeom::MetaData;
77 using Alembic::AbcGeom::P3fArraySamplePtr;
78 using Alembic::AbcGeom::kWrapExisting;
80 using Alembic::AbcGeom::ICamera;
81 using Alembic::AbcGeom::ICurves;
82 using Alembic::AbcGeom::ICurvesSchema;
83 using Alembic::AbcGeom::IFaceSet;
84 using Alembic::AbcGeom::ILight;
85 using Alembic::AbcGeom::INuPatch;
86 using Alembic::AbcGeom::IObject;
87 using Alembic::AbcGeom::IPoints;
88 using Alembic::AbcGeom::IPointsSchema;
89 using Alembic::AbcGeom::IPolyMesh;
90 using Alembic::AbcGeom::IPolyMeshSchema;
91 using Alembic::AbcGeom::ISampleSelector;
92 using Alembic::AbcGeom::ISubD;
93 using Alembic::AbcGeom::IV2fGeomParam;
94 using Alembic::AbcGeom::IXform;
95 using Alembic::AbcGeom::IXformSchema;
96 using Alembic::AbcGeom::N3fArraySamplePtr;
97 using Alembic::AbcGeom::XformSample;
98 using Alembic::AbcGeom::ICompoundProperty;
99 using Alembic::AbcGeom::IN3fArrayProperty;
100 using Alembic::AbcGeom::IN3fGeomParam;
101 using Alembic::AbcGeom::V3fArraySamplePtr;
103 using Alembic::AbcMaterial::IMaterial;
105 struct AbcArchiveHandle {
109 ABC_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle)
111 return reinterpret_cast<ArchiveReader *>(handle);
114 ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive)
116 return reinterpret_cast<AbcArchiveHandle *>(archive);
121 /* NOTE: this function is similar to visit_objects below, need to keep them in
123 static void gather_objects_paths(const IObject &object, ListBase *object_paths)
125 if (!object.valid()) {
129 for (int i = 0; i < object.getNumChildren(); ++i) {
130 IObject child = object.getChild(i);
132 if (!child.valid()) {
136 bool get_path = false;
138 const MetaData &md = child.getMetaData();
140 if (IXform::matches(md)) {
141 /* Check whether or not this object is a Maya locator, which is
142 * similar to empties used as parent object in Blender. */
143 if (has_property(child.getProperties(), "locator")) {
147 /* Avoid creating an empty object if the child of this transform
148 * is not a transform (that is an empty). */
149 if (child.getNumChildren() == 1) {
150 if (IXform::matches(child.getChild(0).getMetaData())) {
155 std::cerr << "gather_objects_paths(" << object.getFullName() << "): Skipping " << child.getFullName() << '\n';
164 else if (IPolyMesh::matches(md)) {
167 else if (ISubD::matches(md)) {
170 else if (INuPatch::matches(md)) {
175 else if (ICamera::matches(md)) {
178 else if (IPoints::matches(md)) {
181 else if (IMaterial::matches(md)) {
184 else if (ILight::matches(md)) {
187 else if (IFaceSet::matches(md)) {
188 /* Pass, those are handled in the mesh reader. */
190 else if (ICurves::matches(md)) {
198 AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
199 MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
201 BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX);
203 BLI_addtail(object_paths, abc_path);
206 gather_objects_paths(child, object_paths);
210 AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths)
212 ArchiveReader *archive = new ArchiveReader(filename);
214 if (!archive->valid()) {
220 gather_objects_paths(archive->getTop(), object_paths);
223 return handle_from_archive(archive);
226 void ABC_free_handle(AbcArchiveHandle *handle)
228 delete archive_from_handle(handle);
231 int ABC_get_version()
233 return ALEMBIC_LIBRARY_VERSION;
236 static void find_iobject(const IObject &object, IObject &ret,
237 const std::string &path)
239 if (!object.valid()) {
243 std::vector<std::string> tokens;
244 split(path, '/', tokens);
246 IObject tmp = object;
248 std::vector<std::string>::iterator iter;
249 for (iter = tokens.begin(); iter != tokens.end(); ++iter) {
250 IObject child = tmp.getChild(*iter);
257 struct ExportJobData {
262 ExportSettings settings;
271 static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
273 ExportJobData *data = static_cast<ExportJobData *>(customdata);
276 data->do_update = do_update;
277 data->progress = progress;
279 /* XXX annoying hack: needed to prevent data corruption when changing
280 * scene frame in separate threads
282 G.is_rendering = true;
283 BKE_spacedata_draw_locks(true);
288 Scene *scene = data->scene;
289 AbcExporter exporter(scene, data->filename, data->settings);
291 const int orig_frame = CFRA;
293 data->was_canceled = false;
294 exporter(data->bmain, *data->progress, data->was_canceled);
296 if (CFRA != orig_frame) {
299 BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain,
303 catch (const std::exception &e) {
304 ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n';
307 ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n";
311 static void export_endjob(void *customdata)
313 ExportJobData *data = static_cast<ExportJobData *>(customdata);
315 if (data->was_canceled && BLI_exists(data->filename)) {
316 BLI_delete(data->filename, false, false);
319 if (!data->settings.logger.empty()) {
320 std::cerr << data->settings.logger;
321 WM_report(RPT_ERROR, "Errors occured during the export, look in the console to know more...");
324 G.is_rendering = false;
325 BKE_spacedata_draw_locks(false);
331 const char *filepath,
332 const struct AlembicExportParams *params)
334 ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
336 job->bmain = CTX_data_main(C);
337 BLI_strncpy(job->filename, filepath, 1024);
339 /* Alright, alright, alright....
341 * ExportJobData contains an ExportSettings containing a SimpleLogger.
343 * Since ExportJobData is a C-style struct dynamically allocated with
344 * MEM_mallocN (see above), its construtor is never called, therefore the
345 * ExportSettings constructor is not called which implies that the
346 * SimpleLogger one is not called either. SimpleLogger in turn does not call
347 * the constructor of its data members which ultimately means that its
348 * std::ostringstream member has a NULL pointer. To be able to properly use
349 * the stream's operator<<, the pointer needs to be set, therefore we have
350 * to properly construct everything. And this is done using the placement
351 * new operator as here below. It seems hackish, but I'm too lazy to
352 * do bigger refactor and maybe there is a better way which does not involve
353 * hardcore refactoring. */
354 new (&job->settings) ExportSettings();
355 job->settings.scene = job->scene;
356 job->settings.frame_start = params->frame_start;
357 job->settings.frame_end = params->frame_end;
358 job->settings.frame_step_xform = params->frame_step_xform;
359 job->settings.frame_step_shape = params->frame_step_shape;
360 job->settings.shutter_open = params->shutter_open;
361 job->settings.shutter_close = params->shutter_close;
362 job->settings.selected_only = params->selected_only;
363 job->settings.export_face_sets = params->face_sets;
364 job->settings.export_normals = params->normals;
365 job->settings.export_uvs = params->uvs;
366 job->settings.export_vcols = params->vcolors;
367 job->settings.apply_subdiv = params->apply_subdiv;
368 job->settings.flatten_hierarchy = params->flatten_hierarchy;
369 job->settings.visible_layers_only = params->visible_layers_only;
370 job->settings.renderable_only = params->renderable_only;
371 job->settings.use_subdiv_schema = params->use_subdiv_schema;
372 job->settings.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA);
373 job->settings.pack_uv = params->packuv;
374 job->settings.global_scale = params->global_scale;
375 job->settings.triangulate = params->triangulate;
376 job->settings.quad_method = params->quad_method;
377 job->settings.ngon_method = params->ngon_method;
379 if (job->settings.frame_start > job->settings.frame_end) {
380 std::swap(job->settings.frame_start, job->settings.frame_end);
383 wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
388 WM_JOB_TYPE_ALEMBIC);
391 WM_jobs_customdata_set(wm_job, job, MEM_freeN);
392 WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
393 WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob);
395 WM_jobs_start(CTX_wm_manager(C), wm_job);
398 /* ********************** Import file ********************** */
401 * Generates an AbcObjectReader for this Alembic object and its children.
403 * \param object The Alembic IObject to visit.
404 * \param readers The created AbcObjectReader * will be appended to this vector.
405 * \param readers_map The created AbcObjectReader * will be appended to this
406 * map, keyed by its full name in Alembic.
407 * \param settings Import settings, not used directly but passed to the
408 * AbcObjectReader subclass constructors.
409 * \return whether this IObject claims its parent as part of the same object
410 * (for example an IPolyMesh object would claim its parent, as the mesh
411 * is interpreted as the object's data, and the parent IXform as its
414 static bool visit_object(const IObject &object,
415 std::vector<AbcObjectReader *> &readers,
417 ImportSettings &settings)
419 const std::string & full_name = object.getFullName();
421 if (!object.valid()) {
422 std::cerr << " - " << full_name << ": object is invalid, skipping it and all its children.\n";
426 // The interpretation of data by the children determine the role of this object.
427 // This is especially important for Xform objects, as they can be either part of a Blender object
428 // or a Blender object (Empty) themselves.
429 size_t children_claiming_this_object = 0;
430 size_t num_children = object.getNumChildren();
431 for (size_t i = 0; i < num_children; ++i) {
432 bool child_claims_this_object = visit_object(object.getChild(i), readers, readers_map, settings);
433 children_claiming_this_object += child_claims_this_object ? 1 : 0;
436 AbcObjectReader *reader = NULL;
437 const MetaData &md = object.getMetaData();
438 bool parent_is_part_of_this_object = false;
440 if (!object.getParent()) {
441 // The root itself is not an object we should import.
443 else if (IXform::matches(md)) {
446 /* An xform can either be a Blender Object (if it contains a mesh, for exapmle),
447 * but it can also be an Empty. Its correct translation to Blender's data model
448 * depends on its children. */
450 /* Check whether or not this object is a Maya locator, which is
451 * similar to empties used as parent object in Blender. */
452 if (has_property(object.getProperties(), "locator")) {
456 create_empty = children_claiming_this_object == 0;
460 reader = new AbcEmptyReader(object, settings);
463 else if (IPolyMesh::matches(md)) {
464 reader = new AbcMeshReader(object, settings);
465 parent_is_part_of_this_object = true;
467 else if (ISubD::matches(md)) {
468 reader = new AbcSubDReader(object, settings);
469 parent_is_part_of_this_object = true;
471 else if (INuPatch::matches(md)) {
473 /* TODO(kevin): importing cyclic NURBS from other software crashes
474 * at the moment. This is due to the fact that NURBS in other
475 * software have duplicated points which causes buffer overflows in
476 * Blender. Need to figure out exactly how these points are
477 * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
478 * Until this is fixed, disabling NURBS reading. */
479 reader = new AbcNurbsReader(object, settings);
480 parent_is_part_of_this_object = true;
483 else if (ICamera::matches(md)) {
484 reader = new AbcCameraReader(object, settings);
485 parent_is_part_of_this_object = true;
487 else if (IPoints::matches(md)) {
488 reader = new AbcPointsReader(object, settings);
489 parent_is_part_of_this_object = true;
491 else if (IMaterial::matches(md)) {
494 else if (ILight::matches(md)) {
497 else if (IFaceSet::matches(md)) {
498 /* Pass, those are handled in the mesh reader. */
500 else if (ICurves::matches(md)) {
501 reader = new AbcCurveReader(object, settings);
502 parent_is_part_of_this_object = true;
505 std::cerr << "Alembic object " << full_name
506 << " is of unsupported schema type '"
507 << object.getMetaData().get("schemaObjTitle") << "'"
512 readers.push_back(reader);
515 AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
516 MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
517 BLI_strncpy(abc_path->path, full_name.c_str(), PATH_MAX);
518 BLI_addtail(&settings.cache_file->object_paths, abc_path);
520 /* We have to take a copy of the name, because Alembic can reuse
521 * memory, for example when dealing with instances. */
522 char * name_copy = static_cast<char *>(MEM_mallocN(
523 full_name.length() + 1,
524 "Alembic readers_map key"));
525 BLI_strncpy(name_copy, full_name.c_str(), full_name.length() + 1);
526 BLI_ghash_insert(readers_map, name_copy, reader);
529 return parent_is_part_of_this_object;
537 struct ImportJobData {
542 ImportSettings settings;
545 std::vector<AbcObjectReader *> readers;
555 ABC_INLINE bool is_mesh_and_strands(const IObject &object)
557 bool has_mesh = false;
558 bool has_curve = false;
560 for (int i = 0; i < object.getNumChildren(); ++i) {
561 const IObject &child = object.getChild(i);
563 if (!child.valid()) {
567 const MetaData &md = child.getMetaData();
569 if (IPolyMesh::matches(md)) {
572 else if (ISubD::matches(md)) {
575 else if (ICurves::matches(md)) {
578 else if (IPoints::matches(md)) {
583 return has_mesh && has_curve;
586 static void import_startjob(void *user_data, short *stop, short *do_update, float *progress)
588 SCOPE_TIMER("Alembic import, objects reading and creation");
590 ImportJobData *data = static_cast<ImportJobData *>(user_data);
593 data->do_update = do_update;
594 data->progress = progress;
596 ArchiveReader *archive = new ArchiveReader(data->filename);
598 if (!archive->valid()) {
600 data->error_code = ABC_ARCHIVE_FAIL;
604 CacheFile *cache_file = static_cast<CacheFile *>(BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename)));
606 /* Decrement the ID ref-count because it is going to be incremented for each
607 * modifier and constraint that it will be attached to, so since currently
608 * it is not used by anyone, its use count will off by one. */
609 id_us_min(&cache_file->id);
611 cache_file->is_sequence = data->settings.is_sequence;
612 cache_file->scale = data->settings.scale;
613 cache_file->handle = handle_from_archive(archive);
614 BLI_strncpy(cache_file->filepath, data->filename, 1024);
616 data->settings.cache_file = cache_file;
618 *data->do_update = true;
619 *data->progress = 0.05f;
621 data->readers_map = BLI_ghash_str_new("Alembic readers_map ghash");
623 /* Parse Alembic Archive. */
624 visit_object(archive->getTop(), data->readers, data->readers_map, data->settings);
627 data->was_cancelled = true;
631 *data->do_update = true;
632 *data->progress = 0.1f;
634 /* Create objects and set scene frame range. */
636 const float size = static_cast<float>(data->readers.size());
639 chrono_t min_time = std::numeric_limits<chrono_t>::max();
640 chrono_t max_time = std::numeric_limits<chrono_t>::min();
642 std::vector<AbcObjectReader *>::iterator iter;
643 for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
644 AbcObjectReader *reader = *iter;
646 if (reader->valid()) {
647 reader->readObjectData(data->bmain, 0.0f);
649 min_time = std::min(min_time, reader->minTime());
650 max_time = std::max(max_time, reader->maxTime());
653 std::cerr << "Object " << reader->name() << " in Alembic file " << data->filename << " is invalid.\n";
656 *data->progress = 0.1f + 0.3f * (++i / size);
657 *data->do_update = true;
660 data->was_cancelled = true;
665 if (data->settings.set_frame_range) {
666 Scene *scene = data->scene;
668 if (data->settings.is_sequence) {
669 SFRA = data->settings.offset;
670 EFRA = SFRA + (data->settings.sequence_len - 1);
673 else if (min_time < max_time) {
674 SFRA = static_cast<int>(min_time * FPS);
675 EFRA = static_cast<int>(max_time * FPS);
680 /* Setup parentship. */
683 for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
684 const AbcObjectReader *reader = *iter;
685 const AbcObjectReader *parent_reader = NULL;
686 const IObject &iobject = reader->iobject();
688 /* Find the parent reader by going up in the Alembic hierarchy until we find it.
689 * Some Xform Alembic objects do not produce an AbcEmptyReader, since they
690 * translate to a Blender object with a reader attached to the Xform's child. */
691 IObject alembic_parent = iobject.getParent();
693 while (alembic_parent) {
694 const char * parent_name = alembic_parent.getFullName().c_str();
695 parent_reader = reinterpret_cast<AbcObjectReader *>(
696 BLI_ghash_lookup(data->readers_map,
698 if (parent_reader != NULL) {
699 break; // found the parent reader.
702 alembic_parent = alembic_parent.getParent();
706 Object *blender_parent = parent_reader->object();
708 if (blender_parent != NULL && reader->object() != blender_parent) {
709 Object *ob = reader->object();
710 ob->parent = blender_parent;
714 *data->progress = 0.4f + 0.3f * (++i / size);
715 *data->do_update = true;
718 data->was_cancelled = true;
723 /* Setup transformations and constraints. */
725 for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
726 AbcObjectReader *reader = *iter;
727 reader->setupObjectTransform(0.0f);
729 *data->progress = 0.7f + 0.3f * (++i / size);
730 *data->do_update = true;
733 data->was_cancelled = true;
739 static void import_endjob(void *user_data)
741 SCOPE_TIMER("Alembic import, cleanup");
743 ImportJobData *data = static_cast<ImportJobData *>(user_data);
745 std::vector<AbcObjectReader *>::iterator iter;
747 /* Delete objects on cancelation. */
748 if (data->was_cancelled) {
749 for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
750 Object *ob = (*iter)->object();
753 BKE_libblock_free_us(data->bmain, ob->data);
757 BKE_libblock_free_us(data->bmain, ob);
761 /* Add object to scene. */
762 BKE_scene_base_deselect_all(data->scene);
764 for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
765 Object *ob = (*iter)->object();
766 ob->lay = data->scene->lay;
768 BKE_scene_base_add(data->scene, ob);
770 DAG_id_tag_update_ex(data->bmain, &ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
773 DAG_relations_tag_update(data->bmain);
776 for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
777 AbcObjectReader *reader = *iter;
780 if (reader->refcount() == 0) {
785 if (data->readers_map) {
786 BLI_ghash_free(data->readers_map, MEM_freeN, NULL);
789 switch (data->error_code) {
793 case ABC_ARCHIVE_FAIL:
794 WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail.");
798 WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene);
801 static void import_freejob(void *user_data)
803 ImportJobData *data = static_cast<ImportJobData *>(user_data);
807 void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, bool validate_meshes)
809 /* Using new here since MEM_* funcs do not call ctor to properly initialize
811 ImportJobData *job = new ImportJobData();
812 job->bmain = CTX_data_main(C);
813 job->scene = CTX_data_scene(C);
814 BLI_strncpy(job->filename, filepath, 1024);
816 job->settings.scale = scale;
817 job->settings.is_sequence = is_sequence;
818 job->settings.set_frame_range = set_frame_range;
819 job->settings.sequence_len = sequence_len;
820 job->settings.offset = offset;
821 job->settings.validate_meshes = validate_meshes;
822 job->readers_map = NULL;
823 job->error_code = ABC_NO_ERROR;
824 job->was_cancelled = false;
828 wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
833 WM_JOB_TYPE_ALEMBIC);
836 WM_jobs_customdata_set(wm_job, job, import_freejob);
837 WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
838 WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob);
840 WM_jobs_start(CTX_wm_manager(C), wm_job);
843 /* ************************************************************************** */
845 void ABC_get_transform(CacheReader *reader, float r_mat[4][4], float time, float scale)
851 AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
853 bool is_constant = false;
854 abc_reader->read_matrix(r_mat, time, scale, is_constant);
857 /* ************************************************************************** */
859 DerivedMesh *ABC_read_mesh(CacheReader *reader,
863 const char **err_str,
866 AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
867 IObject iobject = abc_reader->iobject();
869 if (!iobject.valid()) {
870 *err_str = "Invalid object: verify object path";
874 const ObjectHeader &header = iobject.getHeader();
876 if (IPolyMesh::matches(header)) {
877 if (ob->type != OB_MESH) {
878 *err_str = "Object type mismatch: object path points to a mesh!";
882 return abc_reader->read_derivedmesh(dm, time, read_flag, err_str);
884 else if (ISubD::matches(header)) {
885 if (ob->type != OB_MESH) {
886 *err_str = "Object type mismatch: object path points to a subdivision mesh!";
890 return abc_reader->read_derivedmesh(dm, time, read_flag, err_str);
892 else if (IPoints::matches(header)) {
893 if (ob->type != OB_MESH) {
894 *err_str = "Object type mismatch: object path points to a point cloud (requires a mesh object)!";
898 return abc_reader->read_derivedmesh(dm, time, read_flag, err_str);
900 else if (ICurves::matches(header)) {
901 if (ob->type != OB_CURVE) {
902 *err_str = "Object type mismatch: object path points to a curve!";
906 return abc_reader->read_derivedmesh(dm, time, read_flag, err_str);
909 *err_str = "Unsupported object type: verify object path"; // or poke developer
913 /* ************************************************************************** */
915 void CacheReader_free(CacheReader *reader)
917 AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
918 abc_reader->decref();
920 if (abc_reader->refcount() == 0) {
925 CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, CacheReader *reader, Object *object, const char *object_path)
927 if (object_path[0] == '\0') {
931 ArchiveReader *archive = archive_from_handle(handle);
933 if (!archive || !archive->valid()) {
938 find_iobject(archive->getTop(), iobject, object_path);
941 CacheReader_free(reader);
944 ImportSettings settings;
945 AbcObjectReader *abc_reader = create_reader(iobject, settings);
946 abc_reader->object(object);
947 abc_reader->incref();
949 return reinterpret_cast<CacheReader *>(abc_reader);