Blender V4.5
alembic_capi.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "../ABC_alembic.h"
10#include "IO_types.hh"
11
12#include <Alembic/AbcGeom/ILight.h>
13#include <Alembic/AbcGeom/INuPatch.h>
14#include <Alembic/AbcMaterial/IMaterial.h>
15
16#include "abc_reader_archive.h"
17#include "abc_reader_camera.h"
18#include "abc_reader_curves.h"
19#include "abc_reader_mesh.h"
20#ifdef USE_NURBS
21# include "abc_reader_nurbs.h"
22#endif
23#include "abc_reader_points.h"
25#include "abc_util.h"
26
27#include "MEM_guardedalloc.h"
28
29#include "DNA_cachefile_types.h"
31#include "DNA_object_types.h"
32#include "DNA_scene_types.h"
33
34#include "BKE_cachefile.hh"
35#include "BKE_context.hh"
36#include "BKE_global.hh"
37#include "BKE_layer.hh"
38#include "BKE_lib_id.hh"
39#include "BKE_object.hh"
40
41#include "DEG_depsgraph.hh"
43
44#include "ED_undo.hh"
45
46#include "BLI_compiler_compat.h"
47#include "BLI_listbase.h"
48#include "BLI_math_matrix.h"
49#include "BLI_path_utils.hh"
50#include "BLI_sort.hh"
51#include "BLI_span.hh"
52#include "BLI_string.h"
53#include "BLI_timeit.hh"
54
55#include "BLT_translation.hh"
56
57#include "WM_api.hh"
58#include "WM_types.hh"
59
60using Alembic::Abc::IV3fArrayProperty;
61using Alembic::Abc::ObjectHeader;
62using Alembic::Abc::PropertyHeader;
63using Alembic::Abc::V3fArraySamplePtr;
64using Alembic::AbcGeom::ICamera;
65using Alembic::AbcGeom::ICurves;
66using Alembic::AbcGeom::IFaceSet;
67using Alembic::AbcGeom::ILight;
68using Alembic::AbcGeom::INuPatch;
69using Alembic::AbcGeom::IObject;
70using Alembic::AbcGeom::IPoints;
71using Alembic::AbcGeom::IPolyMesh;
72using Alembic::AbcGeom::IPolyMeshSchema;
73using Alembic::AbcGeom::ISampleSelector;
74using Alembic::AbcGeom::ISubD;
75using Alembic::AbcGeom::IXform;
76using Alembic::AbcGeom::kWrapExisting;
77using Alembic::AbcGeom::MetaData;
78using Alembic::AbcMaterial::IMaterial;
79
80using namespace blender::io::alembic;
81
96
98{
99 return reinterpret_cast<AlembicArchiveData *>(handle);
100}
101
103{
104 return reinterpret_cast<CacheArchiveHandle *>(archive);
105}
106
107/* Add the object's path to list of object paths. No duplication is done, callers are
108 * responsible for ensuring that only unique paths are added to the list.
109 */
110static void add_object_path(ListBase *object_paths, const IObject &object)
111{
112 CacheObjectPath *abc_path = MEM_callocN<CacheObjectPath>("CacheObjectPath");
113 STRNCPY(abc_path->path, object.getFullName().c_str());
114 BLI_addtail(object_paths, abc_path);
115}
116
117// #define USE_NURBS
118
119/* NOTE: this function is similar to visit_objects below, need to keep them in
120 * sync. */
121static bool gather_objects_paths(const IObject &object, ListBase *object_paths)
122{
123 if (!object.valid()) {
124 return false;
125 }
126
127 size_t children_claiming_this_object = 0;
128 size_t num_children = object.getNumChildren();
129
130 for (size_t i = 0; i < num_children; i++) {
131 bool child_claims_this_object = gather_objects_paths(object.getChild(i), object_paths);
132 children_claiming_this_object += child_claims_this_object ? 1 : 0;
133 }
134
135 const MetaData &md = object.getMetaData();
136 bool get_path = false;
137 bool parent_is_part_of_this_object = false;
138
139 if (!object.getParent()) {
140 /* The root itself is not an object we should import. */
141 }
142 else if (IXform::matches(md)) {
143 if (has_property(object.getProperties(), "locator")) {
144 get_path = true;
145 }
146 else {
147 get_path = children_claiming_this_object == 0;
148 }
149
150 /* Transforms are never "data" for their parent. */
151 parent_is_part_of_this_object = false;
152 }
153 else {
154 /* These types are "data" for their parent. */
155 get_path = IPolyMesh::matches(md) || ISubD::matches(md) ||
156#ifdef USE_NURBS
157 INuPatch::matches(md) ||
158#endif
159 ICamera::matches(md) || IPoints::matches(md) || ICurves::matches(md);
160 parent_is_part_of_this_object = get_path;
161 }
162
163 if (get_path) {
164 add_object_path(object_paths, object);
165 }
166
167 return parent_is_part_of_this_object;
168}
169
171 const char *filepath,
172 const CacheFileLayer *layers,
173 ListBase *object_paths)
174{
175 std::vector<const char *> filepaths;
176 filepaths.push_back(filepath);
177
178 while (layers) {
179 if ((layers->flag & CACHEFILE_LAYER_HIDDEN) == 0) {
180 filepaths.push_back(layers->filepath);
181 }
182 layers = layers->next;
183 }
184
185 /* We need to reverse the order as overriding archives should come first. */
186 std::reverse(filepaths.begin(), filepaths.end());
187
188 ArchiveReader *archive = ArchiveReader::get(bmain, filepaths);
189
190 if (!archive || !archive->valid()) {
191 delete archive;
192 return nullptr;
193 }
194
195 if (object_paths) {
196 gather_objects_paths(archive->getTop(), object_paths);
197 }
198
199 AlembicArchiveData *archive_data = new AlembicArchiveData();
200 archive_data->archive_reader = archive;
201 archive_data->settings = new ImportSettings();
202
203 return handle_from_archive(archive_data);
204}
205
207{
208 delete archive_from_handle(handle);
209}
210
212{
213 return ALEMBIC_LIBRARY_VERSION;
214}
215
216static void find_iobject(const IObject &object, IObject &ret, const std::string &path)
217{
218 if (!object.valid()) {
219 return;
220 }
221
222 std::vector<std::string> tokens;
223 split(path, '/', tokens);
224
225 IObject tmp = object;
226
227 std::vector<std::string>::iterator iter;
228 for (iter = tokens.begin(); iter != tokens.end(); ++iter) {
229 IObject child = tmp.getChild(*iter);
230 tmp = child;
231 }
232
233 ret = tmp;
234}
235
236/* ********************** Import file ********************** */
237
258static std::pair<bool, AbcObjectReader *> visit_object(
259 const IObject &object,
261 ImportSettings &settings,
262 AbcObjectReader::ptr_vector &r_assign_as_parent)
263{
264 const std::string &full_name = object.getFullName();
265
266 if (!object.valid()) {
267 std::cerr << " - " << full_name << ": object is invalid, skipping it and all its children.\n";
268 return std::make_pair(false, static_cast<AbcObjectReader *>(nullptr));
269 }
270
271 /* The interpretation of data by the children determine the role of this
272 * object. This is especially important for Xform objects, as they can be
273 * either part of a Blender object or a Blender object (Empty) themselves.
274 */
275 size_t children_claiming_this_object = 0;
276 size_t num_children = object.getNumChildren();
277 AbcObjectReader::ptr_vector claiming_child_readers;
278 AbcObjectReader::ptr_vector nonclaiming_child_readers;
279 AbcObjectReader::ptr_vector assign_as_parent;
280 for (size_t i = 0; i < num_children; i++) {
281 const IObject ichild = object.getChild(i);
282
283 /* TODO: When we only support C++11, use std::tie() instead. */
284 std::pair<bool, AbcObjectReader *> child_result;
285 child_result = visit_object(ichild, readers, settings, assign_as_parent);
286
287 bool child_claims_this_object = child_result.first;
288 AbcObjectReader *child_reader = child_result.second;
289
290 if (child_reader == nullptr) {
291 BLI_assert(!child_claims_this_object);
292 }
293 else {
294 if (child_claims_this_object) {
295 claiming_child_readers.push_back(child_reader);
296 }
297 else {
298 nonclaiming_child_readers.push_back(child_reader);
299 }
300 }
301
302 children_claiming_this_object += child_claims_this_object ? 1 : 0;
303 }
304 BLI_assert(children_claiming_this_object == claiming_child_readers.size());
305 UNUSED_VARS_NDEBUG(children_claiming_this_object);
306
307 AbcObjectReader *reader = nullptr;
308 const MetaData &md = object.getMetaData();
309 bool parent_is_part_of_this_object = false;
310
311 if (!object.getParent()) {
312 /* The root itself is not an object we should import. */
313 }
314 else if (IXform::matches(md)) {
315 bool create_empty;
316
317 /* An xform can either be a Blender Object (if it contains a mesh, for
318 * example), but it can also be an Empty. Its correct translation to
319 * Blender's data model depends on its children. */
320
321 /* Check whether or not this object is a Maya locator, which is
322 * similar to empties used as parent object in Blender. */
323 if (has_property(object.getProperties(), "locator")) {
324 create_empty = true;
325 }
326 else {
327 create_empty = claiming_child_readers.empty();
328 }
329
330 if (create_empty) {
331 reader = new AbcEmptyReader(object, settings);
332 }
333 }
334 else if (IPolyMesh::matches(md)) {
335 reader = new AbcMeshReader(object, settings);
336 parent_is_part_of_this_object = true;
337 }
338 else if (ISubD::matches(md)) {
339 reader = new AbcSubDReader(object, settings);
340 parent_is_part_of_this_object = true;
341 }
342 else if (INuPatch::matches(md)) {
343#ifdef USE_NURBS
344 /* TODO(kevin): importing cyclic NURBS from other software crashes
345 * at the moment. This is due to the fact that NURBS in other
346 * software have duplicated points which causes buffer overflows in
347 * Blender. Need to figure out exactly how these points are
348 * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
349 * Until this is fixed, disabling NURBS reading. */
350 reader = new AbcNurbsReader(object, settings);
351 parent_is_part_of_this_object = true;
352#endif
353 }
354 else if (ICamera::matches(md)) {
355 reader = new AbcCameraReader(object, settings);
356 parent_is_part_of_this_object = true;
357 }
358 else if (IPoints::matches(md)) {
359 reader = new AbcPointsReader(object, settings);
360 parent_is_part_of_this_object = true;
361 }
362 else if (IMaterial::matches(md)) {
363 /* Pass for now. */
364 }
365 else if (ILight::matches(md)) {
366 /* Pass for now. */
367 }
368 else if (IFaceSet::matches(md)) {
369 /* Pass, those are handled in the mesh reader. */
370 }
371 else if (ICurves::matches(md)) {
372 reader = new AbcCurveReader(object, settings);
373 parent_is_part_of_this_object = true;
374 }
375 else {
376 std::cerr << "Alembic object " << full_name << " is of unsupported schema type '"
377 << object.getMetaData().get("schemaObjTitle") << "'" << std::endl;
378 }
379
380 if (reader) {
381 /* We have created a reader, which should imply that this object is
382 * not claimed as part of any child Alembic object. */
383 BLI_assert(claiming_child_readers.empty());
384
385 readers.push_back(reader);
386 reader->incref();
387
388 add_object_path(&settings.cache_file->object_paths, object);
389
390 /* We can now assign this reader as parent for our children. */
391 if (nonclaiming_child_readers.size() + assign_as_parent.size() > 0) {
392 for (AbcObjectReader *child_reader : nonclaiming_child_readers) {
393 child_reader->parent_reader = reader;
394 }
395 for (AbcObjectReader *child_reader : assign_as_parent) {
396 child_reader->parent_reader = reader;
397 }
398 }
399 }
400 else if (object.getParent()) {
401 if (!claiming_child_readers.empty()) {
402 /* The first claiming child will serve just fine as parent to
403 * our non-claiming children. Since all claiming children share
404 * the same XForm, it doesn't really matter which one we pick. */
405 AbcObjectReader *claiming_child = claiming_child_readers[0];
406 for (AbcObjectReader *child_reader : nonclaiming_child_readers) {
407 child_reader->parent_reader = claiming_child;
408 }
409 for (AbcObjectReader *child_reader : assign_as_parent) {
410 child_reader->parent_reader = claiming_child;
411 }
412 /* Claiming children should have our parent set as their parent. */
413 for (AbcObjectReader *child_reader : claiming_child_readers) {
414 r_assign_as_parent.push_back(child_reader);
415 }
416 }
417 else {
418 /* This object isn't claimed by any child, and didn't produce
419 * a reader. Odd situation, could be the top Alembic object, or
420 * an unsupported Alembic schema. Delegate to our parent. */
421 for (AbcObjectReader *child_reader : claiming_child_readers) {
422 r_assign_as_parent.push_back(child_reader);
423 }
424 for (AbcObjectReader *child_reader : nonclaiming_child_readers) {
425 r_assign_as_parent.push_back(child_reader);
426 }
427 for (AbcObjectReader *child_reader : assign_as_parent) {
428 r_assign_as_parent.push_back(child_reader);
429 }
430 }
431 }
432
433 return std::make_pair(parent_is_part_of_this_object, reader);
434}
435
436enum {
439};
440
470
472{
473 blender::timeit::Nanoseconds duration = blender::timeit::Clock::now() - data->start_time;
474 std::cout << "Alembic import took ";
476 std::cout << '\n';
477}
478
480{
482 readers.begin(), readers.end(), [](const AbcObjectReader *a, const AbcObjectReader *b) {
483 const char *na = a->name().c_str();
484 const char *nb = b->name().c_str();
485 return BLI_strcasecmp(na, nb) < 0;
486 });
487}
488
489static void import_file(ImportJobData *data, const char *filepath, float progress_factor)
490{
491 blender::timeit::TimePoint start_time = blender::timeit::Clock::now();
492 SCOPE_TIMER("Alembic import, objects reading and creation");
493
494 ArchiveReader *archive = ArchiveReader::get(data->bmain, {filepath});
495
496 if (!archive || !archive->valid()) {
497 data->error_code = ABC_ARCHIVE_FAIL;
498 delete archive;
499 return;
500 }
501
502 CacheFile *cache_file = static_cast<CacheFile *>(
503 BKE_cachefile_add(data->bmain, BLI_path_basename(filepath)));
504
505 /* Decrement the ID ref-count because it is going to be incremented for each
506 * modifier and constraint that it will be attached to, so since currently
507 * it is not used by anyone, its use count will be off by one. */
508 id_us_min(&cache_file->id);
509
510 cache_file->is_sequence = data->settings.is_sequence;
511 cache_file->scale = data->settings.scale;
512 STRNCPY(cache_file->filepath, filepath);
513
514 data->archives.append(archive);
515 data->settings.cache_file = cache_file;
516 data->settings.blender_archive_version_prior_44 = archive->is_blender_archive_version_prior_44();
517
518 *data->do_update = true;
519 *data->progress += 0.05f * progress_factor;
520
521 /* Parse Alembic Archive. */
522 AbcObjectReader::ptr_vector assign_as_parent;
523 std::vector<AbcObjectReader *> readers{};
524 visit_object(archive->getTop(), readers, data->settings, assign_as_parent);
525
526 /* There shouldn't be any orphans. */
527 BLI_assert(assign_as_parent.empty());
528
529 if (G.is_break) {
530 data->was_cancelled = true;
531 data->readers.extend(readers);
532 return;
533 }
534
535 *data->do_update = true;
536 *data->progress += 0.05f * progress_factor;
537
538 /* Create objects and set scene frame range. */
539
540 /* Sort readers by name: when creating a lot of objects in Blender,
541 * it is much faster if the order is sorted by name. */
542 sort_readers(readers);
543 data->readers.extend(readers);
544
545 const float size = float(readers.size());
546
547 ISampleSelector sample_sel(0.0);
548 std::vector<AbcObjectReader *>::iterator iter;
549 const float read_object_progress_step = (0.6f / size) * progress_factor;
550 for (iter = readers.begin(); iter != readers.end(); ++iter) {
551 AbcObjectReader *reader = *iter;
552
553 if (reader->valid()) {
554 reader->readObjectData(data->bmain, sample_sel);
555
556 data->min_time = std::min(data->min_time, reader->minTime());
557 data->max_time = std::max(data->max_time, reader->maxTime());
558 }
559 else {
560 std::cerr << "Object " << reader->name() << " in Alembic file " << filepath
561 << " is invalid.\n";
562 }
563 *data->progress += read_object_progress_step;
564 *data->do_update = true;
565
566 if (G.is_break) {
567 data->was_cancelled = true;
568 return;
569 }
570 }
571
572 /* Setup parenthood. */
573 for (iter = readers.begin(); iter != readers.end(); ++iter) {
574 const AbcObjectReader *reader = *iter;
575 const AbcObjectReader *parent_reader = reader->parent_reader;
576 Object *ob = reader->object();
577
578 if (parent_reader == nullptr || !reader->inherits_xform()) {
579 ob->parent = nullptr;
580 }
581 else {
582 ob->parent = parent_reader->object();
583 }
584 }
585
586 /* Setup transformations and constraints. */
587 const float setup_object_transform_progress_step = (0.3f / size) * progress_factor;
588 for (iter = readers.begin(); iter != readers.end(); ++iter) {
589 AbcObjectReader *reader = *iter;
590 reader->setupObjectTransform(0.0);
591
592 *data->progress += setup_object_transform_progress_step;
593 *data->do_update = true;
594
595 if (G.is_break) {
596 data->was_cancelled = true;
597 return;
598 }
599 }
600 blender::timeit::Nanoseconds duration = blender::timeit::Clock::now() - start_time;
601 std::cout << "Alembic import " << filepath << " took ";
603 std::cout << '\n';
604}
605
607{
608 if (!data->settings.set_frame_range) {
609 return;
610 }
611 Scene *scene = data->scene;
612 if (data->settings.is_sequence) {
613 scene->r.sfra = data->settings.sequence_min_frame;
614 scene->r.efra = data->settings.sequence_max_frame;
615 scene->r.cfra = scene->r.sfra;
616 }
617 else if (data->min_time < data->max_time) {
618 scene->r.sfra = int(round(data->min_time * FPS));
619 scene->r.efra = int(round(data->max_time * FPS));
620 scene->r.cfra = scene->r.sfra;
621 }
622}
623
624static void import_startjob(void *user_data, wmJobWorkerStatus *worker_status)
625{
626 ImportJobData *data = static_cast<ImportJobData *>(user_data);
627 data->stop = &worker_status->stop;
628 data->do_update = &worker_status->do_update;
629 data->progress = &worker_status->progress;
630 data->start_time = blender::timeit::Clock::now();
631
632 WM_set_locked_interface(data->wm, true);
633 float file_progress_factor = 1.0f / float(data->paths.size());
634 for (int idx : data->paths.index_range()) {
635 import_file(data, data->paths[idx].c_str(), file_progress_factor);
636
637 if (G.is_break || data->was_cancelled) {
638 data->was_cancelled = true;
639 return;
640 }
641
642 worker_status->progress = float(idx + 1) * file_progress_factor;
643 }
645}
646
647static void import_endjob(void *user_data)
648{
649 SCOPE_TIMER("Alembic import, cleanup");
650
651 ImportJobData *data = static_cast<ImportJobData *>(user_data);
652
653 /* Delete objects on cancellation. */
654 if (data->was_cancelled) {
655 for (AbcObjectReader *reader : data->readers) {
656 Object *ob = reader->object();
657
658 /* It's possible that cancellation occurred between the creation of
659 * the reader and the creation of the Blender object. */
660 if (ob == nullptr) {
661 continue;
662 }
663
664 BKE_id_free_us(data->bmain, ob);
665 }
666 }
667 else {
668 Base *base;
669 LayerCollection *lc;
670 const Scene *scene = data->scene;
671 ViewLayer *view_layer = data->view_layer;
672
673 BKE_view_layer_base_deselect_all(scene, view_layer);
674
675 lc = BKE_layer_collection_get_active(view_layer);
676
677 for (AbcObjectReader *reader : data->readers) {
678 Object *ob = reader->object();
680 }
681 /* Sync and do the view layer operations. */
682 BKE_view_layer_synced_ensure(scene, view_layer);
683 for (AbcObjectReader *reader : data->readers) {
684 Object *ob = reader->object();
685 base = BKE_view_layer_base_find(view_layer, ob);
686 /* TODO: is setting active needed? */
688
691 &ob->id,
694 }
695
698
699 if (data->is_background_job) {
700 /* Blender already returned from the import operator, so we need to store our own extra undo
701 * step. */
702 ED_undo_push(data->C, "Alembic Import Finished");
703 }
704 }
705
706 for (AbcObjectReader *reader : data->readers) {
707 reader->decref();
708
709 if (reader->refcount() == 0) {
710 delete reader;
711 }
712 }
713
714 WM_set_locked_interface(data->wm, false);
715
716 switch (data->error_code) {
717 default:
718 case ABC_NO_ERROR:
719 data->import_ok = !data->was_cancelled;
720 break;
721 case ABC_ARCHIVE_FAIL:
723 "Could not open Alembic archive for reading, see console for detail");
724 break;
725 }
726
729}
730
731static void import_freejob(void *user_data)
732{
733 ImportJobData *data = static_cast<ImportJobData *>(user_data);
734 for (ArchiveReader *archive : data->archives) {
735 delete archive;
736 }
737 delete data;
738}
739
740bool ABC_import(bContext *C, const AlembicImportParams *params, bool as_background_job)
741{
742 /* Using new here since MEM_* functions do not call constructor to properly initialize data. */
743 ImportJobData *job = new ImportJobData();
744 job->C = C;
745 job->bmain = CTX_data_main(C);
746 job->scene = CTX_data_scene(C);
748 job->wm = CTX_wm_manager(C);
749 job->import_ok = false;
750 job->paths = params->paths;
751
752 job->settings.scale = params->global_scale;
753 job->settings.is_sequence = params->is_sequence;
754 job->settings.set_frame_range = params->set_frame_range;
755 job->settings.sequence_min_frame = params->sequence_min_frame;
756 job->settings.sequence_max_frame = params->sequence_max_frame;
757 job->settings.validate_meshes = params->validate_meshes;
758 job->settings.always_add_cache_reader = params->always_add_cache_reader;
760 job->was_cancelled = false;
761 job->is_background_job = as_background_job;
762
763 G.is_break = false;
764
765 bool import_ok = false;
766 if (as_background_job) {
769 job->scene,
770 "Alembic Import",
773
774 /* setup job */
777 WM_jobs_callbacks(wm_job, import_startjob, nullptr, nullptr, import_endjob);
778
780 }
781 else {
782 wmJobWorkerStatus worker_status = {};
783 import_startjob(job, &worker_status);
784 import_endjob(job);
785 import_ok = job->import_ok;
786
787 import_freejob(job);
788 }
789
790 return import_ok;
791}
792
793/* ************************************************************************** */
794
795void ABC_get_transform(CacheReader *reader, float r_mat_world[4][4], double time, float scale)
796{
797 if (!reader) {
798 return;
799 }
800
801 AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
802
803 bool is_constant = false;
804
805 /* Convert from the local matrix we obtain from Alembic to world coordinates
806 * for Blender. This conversion is done here rather than by Blender due to
807 * work around the non-standard interpretation of CONSTRAINT_SPACE_LOCAL in
808 * BKE_constraint_mat_convertspace(). */
809 Object *object = abc_reader->object();
810 if (object->parent == nullptr) {
811 /* No parent, so local space is the same as world space. */
812 abc_reader->read_matrix(r_mat_world, time, scale, is_constant);
813 return;
814 }
815
816 float mat_parent[4][4];
817 BKE_object_get_parent_matrix(object, object->parent, mat_parent);
818
819 float mat_local[4][4];
820 abc_reader->read_matrix(mat_local, time, scale, is_constant);
821 mul_m4_m4m4(r_mat_world, mat_parent, object->parentinv);
822 mul_m4_m4m4(r_mat_world, r_mat_world, mat_local);
823}
824
825/* ************************************************************************** */
826
827static AbcObjectReader *get_abc_reader(CacheReader *reader, Object *ob, const char **r_err_str)
828{
829 AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
830 IObject iobject = abc_reader->iobject();
831
832 if (!iobject.valid()) {
833 *r_err_str = RPT_("Invalid object: verify object path");
834 return nullptr;
835 }
836
837 const ObjectHeader &header = iobject.getHeader();
838 if (!abc_reader->accepts_object_type(header, ob, r_err_str)) {
839 /* r_err_str is set by acceptsObjectType() */
840 return nullptr;
841 }
842
843 return abc_reader;
844}
845
846static ISampleSelector sample_selector_for_time(chrono_t time)
847{
848 /* kFloorIndex is used to be compatible with non-interpolating
849 * properties; they use the floor. */
850 return ISampleSelector(time, ISampleSelector::kFloorIndex);
851}
852
854 Object *ob,
855 blender::bke::GeometrySet &geometry_set,
856 const ABCReadParams *params,
857 const char **r_err_str)
858{
859 AbcObjectReader *abc_reader = get_abc_reader(reader, ob, r_err_str);
860 if (abc_reader == nullptr) {
861 return;
862 }
863
864 ISampleSelector sample_sel = sample_selector_for_time(params->time);
865 abc_reader->read_geometry(geometry_set,
866 sample_sel,
867 params->read_flags,
868 params->velocity_name,
869 params->velocity_scale,
870 r_err_str);
871}
872
874 Object *ob,
875 const Mesh *existing_mesh,
876 const double time,
877 const char **r_err_str)
878{
879 AbcObjectReader *abc_reader = get_abc_reader(reader, ob, r_err_str);
880 if (abc_reader == nullptr) {
881 return false;
882 }
883
884 ISampleSelector sample_sel = sample_selector_for_time(time);
885 return abc_reader->topology_changed(existing_mesh, sample_sel);
886}
887
888/* ************************************************************************** */
889
891{
892 AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
893 abc_reader->decref();
894
895 if (abc_reader->refcount() == 0) {
896 delete abc_reader;
897 }
898}
899
901 CacheReader *reader,
902 Object *object,
903 const char *object_path,
904 const bool is_sequence)
905{
906 if (object_path[0] == '\0') {
907 return reader;
908 }
909
910 AlembicArchiveData *archive_data = archive_from_handle(handle);
911 if (!archive_data) {
912 return reader;
913 }
914
915 ArchiveReader *archive = archive_data->archive_reader;
916 if (!archive || !archive->valid()) {
917 return reader;
918 }
919
920 IObject iobject;
921 find_iobject(archive->getTop(), iobject, object_path);
922
923 if (reader) {
924 ABC_CacheReader_free(reader);
925 }
926
927 archive_data->settings->is_sequence = is_sequence;
930
931 AbcObjectReader *abc_reader = create_reader(iobject, *archive_data->settings);
932 if (abc_reader == nullptr) {
933 /* This object is not supported */
934 return nullptr;
935 }
936 abc_reader->object(object);
937 abc_reader->incref();
938
939 return reinterpret_cast<CacheReader *>(abc_reader);
940}
void * BKE_cachefile_add(Main *bmain, const char *name)
Definition cachefile.cc:308
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
LayerCollection * BKE_layer_collection_get_active(ViewLayer *view_layer)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
void BKE_view_layer_base_deselect_all(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
void BKE_view_layer_base_select_and_set_active(ViewLayer *view_layer, Base *selbase)
void BKE_id_free_us(Main *bmain, void *idv) ATTR_NONNULL()
void id_us_min(ID *id)
Definition lib_id.cc:361
General operations, lookup, etc. for blender objects.
void BKE_object_get_parent_matrix(const Object *ob, Object *par, float r_parentmat[4][4])
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define UNUSED_VARS_NDEBUG(...)
#define RPT_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:962
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:985
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ ID_RECALC_BASE_FLAGS
Definition DNA_ID.h:1012
@ CACHEFILE_LAYER_HIDDEN
Object groups, one object can be in many groups at once.
Object is a sort of wrapper for general info.
#define FPS
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:99
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_ALEMBIC_IMPORT
Definition WM_api.hh:1746
@ WM_JOB_PROGRESS
Definition WM_api.hh:1716
#define NC_ID
Definition WM_types.hh:392
#define NC_SCENE
Definition WM_types.hh:375
#define NA_ADDED
Definition WM_types.hh:583
#define ND_FRAME
Definition WM_types.hh:431
#define SCOPE_TIMER(message)
Definition abc_util.h:142
static void import_freejob(void *user_data)
static std::pair< bool, AbcObjectReader * > visit_object(const IObject &object, AbcObjectReader::ptr_vector &readers, ImportSettings &settings, AbcObjectReader::ptr_vector &r_assign_as_parent)
static ISampleSelector sample_selector_for_time(chrono_t time)
void ABC_read_geometry(CacheReader *reader, Object *ob, blender::bke::GeometrySet &geometry_set, const ABCReadParams *params, const char **r_err_str)
@ ABC_ARCHIVE_FAIL
@ ABC_NO_ERROR
static void import_file(ImportJobData *data, const char *filepath, float progress_factor)
void ABC_get_transform(CacheReader *reader, float r_mat_world[4][4], double time, float scale)
void ABC_free_handle(CacheArchiveHandle *handle)
CacheReader * CacheReader_open_alembic_object(CacheArchiveHandle *handle, CacheReader *reader, Object *object, const char *object_path, const bool is_sequence)
static bool gather_objects_paths(const IObject &object, ListBase *object_paths)
static AbcObjectReader * get_abc_reader(CacheReader *reader, Object *ob, const char **r_err_str)
BLI_INLINE CacheArchiveHandle * handle_from_archive(AlembicArchiveData *archive)
static void sort_readers(blender::MutableSpan< AbcObjectReader * > readers)
int ABC_get_version()
bool ABC_mesh_topology_changed(CacheReader *reader, Object *ob, const Mesh *existing_mesh, const double time, const char **r_err_str)
static void import_startjob(void *user_data, wmJobWorkerStatus *worker_status)
bool ABC_import(bContext *C, const AlembicImportParams *params, bool as_background_job)
BLI_INLINE AlembicArchiveData * archive_from_handle(CacheArchiveHandle *handle)
CacheArchiveHandle * ABC_create_handle(const Main *bmain, const char *filepath, const CacheFileLayer *layers, ListBase *object_paths)
void ABC_CacheReader_free(CacheReader *reader)
static void import_endjob(void *user_data)
static void add_object_path(ListBase *object_paths, const IObject &object)
static void find_iobject(const IObject &object, IObject &ret, const std::string &path)
static void set_frame_range(ImportJobData *data)
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int getParent(int link_num) const
constexpr T * end() const
Definition BLI_span.hh:548
constexpr T * begin() const
Definition BLI_span.hh:544
void read_matrix(float r_mat[4][4], chrono_t time, float scale, bool &is_constant)
const Alembic::Abc::IObject & iobject() const
virtual bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, const char **r_err_str) const =0
virtual bool topology_changed(const Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel)
virtual void read_geometry(bke::GeometrySet &geometry_set, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char *velocity_name, float velocity_scale, const char **r_err_str)
virtual void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)=0
std::vector< AbcObjectReader * > ptr_vector
static ArchiveReader * get(const struct Main *bmain, const std::vector< const char * > &filenames)
#define round
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
#define G(x, y, z)
bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name)
Definition abc_util.cc:111
AbcObjectReader * create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings)
Definition abc_util.cc:191
void split(const std::string &s, const char delim, std::vector< std::string > &tokens)
Definition abc_util.cc:97
static void report_job_duration(const ExportJobData *data)
std::chrono::nanoseconds Nanoseconds
Definition BLI_timeit.hh:21
Clock::time_point TimePoint
Definition BLI_timeit.hh:20
void print_duration(Nanoseconds duration)
Definition timeit.cc:45
void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end)
Definition BLI_sort.hh:23
return ret
ImportSettings * settings
AlembicArchiveData(const AlembicArchiveData &)=delete
AlembicArchiveData()=default
AlembicArchiveData & operator==(const AlembicArchiveData &)=delete
ArchiveReader * archive_reader
struct CacheFileLayer * next
ListBase object_paths
char filepath[1024]
blender::timeit::TimePoint start_time
wmWindowManager * wm
ViewLayer * view_layer
blender::Vector< ArchiveReader * > archives
ImportSettings settings
chrono_t max_time
blender::Vector< std::string > paths
blender::Vector< AbcObjectReader * > readers
chrono_t min_time
struct Collection * collection
float parentinv[4][4]
struct Object * parent
struct RenderData r
i
Definition text_draw.cc:230
void WM_global_report(eReportType type, const char *message)
void WM_main_add_notifier(uint type, void *reference)
void WM_set_locked_interface(wmWindowManager *wm, bool lock)
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:353
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:456
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:190
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:365
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:337