Blender V5.0
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
493 ArchiveReader *archive = ArchiveReader::get(data->bmain, {filepath});
494
495 if (!archive || !archive->valid()) {
496 data->error_code = ABC_ARCHIVE_FAIL;
497 delete archive;
498 return;
499 }
500
501 CacheFile *cache_file = static_cast<CacheFile *>(
502 BKE_cachefile_add(data->bmain, BLI_path_basename(filepath)));
503
504 /* Decrement the ID ref-count because it is going to be incremented for each
505 * modifier and constraint that it will be attached to, so since currently
506 * it is not used by anyone, its use count will be off by one. */
507 id_us_min(&cache_file->id);
508
509 cache_file->is_sequence = data->settings.is_sequence;
510 cache_file->scale = data->settings.scale;
511 STRNCPY(cache_file->filepath, filepath);
512
513 data->archives.append(archive);
514 data->settings.cache_file = cache_file;
515 data->settings.blender_archive_version_prior_44 = archive->is_blender_archive_version_prior_44();
516
517 *data->do_update = true;
518 *data->progress += 0.05f * progress_factor;
519
520 /* Parse Alembic Archive. */
521 AbcObjectReader::ptr_vector assign_as_parent;
522 std::vector<AbcObjectReader *> readers{};
523 visit_object(archive->getTop(), readers, data->settings, assign_as_parent);
524
525 /* There shouldn't be any orphans. */
526 BLI_assert(assign_as_parent.empty());
527
528 if (G.is_break) {
529 data->was_cancelled = true;
530 data->readers.extend(readers);
531 return;
532 }
533
534 *data->do_update = true;
535 *data->progress += 0.05f * progress_factor;
536
537 /* Create objects and set scene frame range. */
538
539 /* Sort readers by name: when creating a lot of objects in Blender,
540 * it is much faster if the order is sorted by name. */
541 sort_readers(readers);
542 data->readers.extend(readers);
543
544 const float size = float(readers.size());
545
546 ISampleSelector sample_sel(0.0);
547 std::vector<AbcObjectReader *>::iterator iter;
548 const float read_object_progress_step = (0.6f / size) * progress_factor;
549 for (iter = readers.begin(); iter != readers.end(); ++iter) {
550 AbcObjectReader *reader = *iter;
551
552 if (reader->valid()) {
553 reader->readObjectData(data->bmain, sample_sel);
554
555 data->min_time = std::min(data->min_time, reader->minTime());
556 data->max_time = std::max(data->max_time, reader->maxTime());
557 }
558 else {
559 std::cerr << "Object " << reader->name() << " in Alembic file " << filepath
560 << " is invalid.\n";
561 }
562 *data->progress += read_object_progress_step;
563 *data->do_update = true;
564
565 if (G.is_break) {
566 data->was_cancelled = true;
567 return;
568 }
569 }
570
571 /* Setup parenthood. */
572 for (iter = readers.begin(); iter != readers.end(); ++iter) {
573 const AbcObjectReader *reader = *iter;
574 const AbcObjectReader *parent_reader = reader->parent_reader;
575 Object *ob = reader->object();
576
577 if (parent_reader == nullptr || !reader->inherits_xform()) {
578 ob->parent = nullptr;
579 }
580 else {
581 ob->parent = parent_reader->object();
582 }
583 }
584
585 /* Setup transformations and constraints. */
586 const float setup_object_transform_progress_step = (0.3f / size) * progress_factor;
587 for (iter = readers.begin(); iter != readers.end(); ++iter) {
588 AbcObjectReader *reader = *iter;
589 reader->setupObjectTransform(0.0);
590
591 *data->progress += setup_object_transform_progress_step;
592 *data->do_update = true;
593
594 if (G.is_break) {
595 data->was_cancelled = true;
596 return;
597 }
598 }
599 blender::timeit::Nanoseconds duration = blender::timeit::Clock::now() - start_time;
600 std::cout << "Alembic import " << filepath << " took ";
602 std::cout << '\n';
603}
604
606{
607 if (!data->settings.set_frame_range) {
608 return;
609 }
610 Scene *scene = data->scene;
611 if (data->settings.is_sequence) {
612 scene->r.sfra = data->settings.sequence_min_frame;
613 scene->r.efra = data->settings.sequence_max_frame;
614 scene->r.cfra = scene->r.sfra;
615 }
616 else if (data->min_time < data->max_time) {
617 scene->r.sfra = int(round(data->min_time * scene->frames_per_second()));
618 scene->r.efra = int(round(data->max_time * scene->frames_per_second()));
619 scene->r.cfra = scene->r.sfra;
620 }
621}
622
623static void import_startjob(void *user_data, wmJobWorkerStatus *worker_status)
624{
625 ImportJobData *data = static_cast<ImportJobData *>(user_data);
626 data->stop = &worker_status->stop;
627 data->do_update = &worker_status->do_update;
628 data->progress = &worker_status->progress;
629 data->start_time = blender::timeit::Clock::now();
630
631 WM_locked_interface_set(data->wm, true);
632 float file_progress_factor = 1.0f / float(data->paths.size());
633 for (int idx : data->paths.index_range()) {
634 import_file(data, data->paths[idx].c_str(), file_progress_factor);
635
636 if (G.is_break || data->was_cancelled) {
637 data->was_cancelled = true;
638 return;
639 }
640
641 worker_status->progress = float(idx + 1) * file_progress_factor;
642 }
644}
645
646static void import_endjob(void *user_data)
647{
648 ImportJobData *data = static_cast<ImportJobData *>(user_data);
649
650 /* Delete objects on cancellation. */
651 if (data->was_cancelled) {
652 for (AbcObjectReader *reader : data->readers) {
653 Object *ob = reader->object();
654
655 /* It's possible that cancellation occurred between the creation of
656 * the reader and the creation of the Blender object. */
657 if (ob == nullptr) {
658 continue;
659 }
660
661 BKE_id_free_us(data->bmain, ob);
662 }
663 }
664 else {
665 Base *base;
666 LayerCollection *lc;
667 const Scene *scene = data->scene;
668 ViewLayer *view_layer = data->view_layer;
669
670 BKE_view_layer_base_deselect_all(scene, view_layer);
671
672 lc = BKE_layer_collection_get_active(view_layer);
673
674 for (AbcObjectReader *reader : data->readers) {
675 Object *ob = reader->object();
677 }
678 /* Sync and do the view layer operations. */
679 BKE_view_layer_synced_ensure(scene, view_layer);
680 for (AbcObjectReader *reader : data->readers) {
681 Object *ob = reader->object();
682 base = BKE_view_layer_base_find(view_layer, ob);
683 /* TODO: is setting active needed? */
685
688 &ob->id,
691 }
692
695
696 if (data->is_background_job) {
697 /* Blender already returned from the import operator, so we need to store our own extra undo
698 * step. */
699 ED_undo_push(data->C, "Alembic Import Finished");
700 }
701 }
702
703 for (AbcObjectReader *reader : data->readers) {
704 reader->decref();
705
706 if (reader->refcount() == 0) {
707 delete reader;
708 }
709 }
710
711 WM_locked_interface_set(data->wm, false);
712
713 switch (data->error_code) {
714 default:
715 case ABC_NO_ERROR:
716 data->import_ok = !data->was_cancelled;
717 break;
718 case ABC_ARCHIVE_FAIL:
720 "Could not open Alembic archive for reading, see console for detail");
721 break;
722 }
723
726}
727
728static void import_freejob(void *user_data)
729{
730 ImportJobData *data = static_cast<ImportJobData *>(user_data);
731 for (ArchiveReader *archive : data->archives) {
732 delete archive;
733 }
734 delete data;
735}
736
737bool ABC_import(bContext *C, const AlembicImportParams *params, bool as_background_job)
738{
739 /* Using new here since MEM_* functions do not call constructor to properly initialize data. */
740 ImportJobData *job = new ImportJobData();
741 job->C = C;
742 job->bmain = CTX_data_main(C);
743 job->scene = CTX_data_scene(C);
745 job->wm = CTX_wm_manager(C);
746 job->import_ok = false;
747 job->paths = params->paths;
748
749 job->settings.scale = params->global_scale;
750 job->settings.is_sequence = params->is_sequence;
751 job->settings.set_frame_range = params->set_frame_range;
752 job->settings.sequence_min_frame = params->sequence_min_frame;
753 job->settings.sequence_max_frame = params->sequence_max_frame;
754 job->settings.validate_meshes = params->validate_meshes;
755 job->settings.always_add_cache_reader = params->always_add_cache_reader;
757 job->was_cancelled = false;
758 job->is_background_job = as_background_job;
759
760 G.is_break = false;
761
762 bool import_ok = false;
763 if (as_background_job) {
766 job->scene,
767 "Importing Alembic...",
770
771 /* setup job */
774 WM_jobs_callbacks(wm_job, import_startjob, nullptr, nullptr, import_endjob);
775
777 }
778 else {
779 wmJobWorkerStatus worker_status = {};
780 import_startjob(job, &worker_status);
781 import_endjob(job);
782 import_ok = job->import_ok;
783
784 import_freejob(job);
785 }
786
787 return import_ok;
788}
789
790/* ************************************************************************** */
791
792void ABC_get_transform(CacheReader *reader, float r_mat_world[4][4], double time, float scale)
793{
794 if (!reader) {
795 return;
796 }
797
798 AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
799
800 bool is_constant = false;
801
802 /* Convert from the local matrix we obtain from Alembic to world coordinates
803 * for Blender. This conversion is done here rather than by Blender due to
804 * work around the non-standard interpretation of CONSTRAINT_SPACE_LOCAL in
805 * BKE_constraint_mat_convertspace(). */
806 Object *object = abc_reader->object();
807 if (object->parent == nullptr) {
808 /* No parent, so local space is the same as world space. */
809 abc_reader->read_matrix(r_mat_world, time, scale, is_constant);
810 return;
811 }
812
813 float mat_parent[4][4];
814 BKE_object_get_parent_matrix(object, object->parent, mat_parent);
815
816 float mat_local[4][4];
817 abc_reader->read_matrix(mat_local, time, scale, is_constant);
818 mul_m4_m4m4(r_mat_world, mat_parent, object->parentinv);
819 mul_m4_m4m4(r_mat_world, r_mat_world, mat_local);
820}
821
822/* ************************************************************************** */
823
824static AbcObjectReader *get_abc_reader(CacheReader *reader, Object *ob, const char **r_err_str)
825{
826 AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
827 IObject iobject = abc_reader->iobject();
828
829 if (!iobject.valid()) {
830 *r_err_str = RPT_("Invalid object: verify object path");
831 return nullptr;
832 }
833
834 const ObjectHeader &header = iobject.getHeader();
835 if (!abc_reader->accepts_object_type(header, ob, r_err_str)) {
836 /* r_err_str is set by acceptsObjectType() */
837 return nullptr;
838 }
839
840 return abc_reader;
841}
842
843static ISampleSelector sample_selector_for_time(chrono_t time)
844{
845 /* kFloorIndex is used to be compatible with non-interpolating
846 * properties; they use the floor. */
847 return ISampleSelector(time, ISampleSelector::kFloorIndex);
848}
849
851 Object *ob,
852 blender::bke::GeometrySet &geometry_set,
853 const ABCReadParams *params,
854 const char **r_err_str)
855{
856 AbcObjectReader *abc_reader = get_abc_reader(reader, ob, r_err_str);
857 if (abc_reader == nullptr) {
858 return;
859 }
860
861 ISampleSelector sample_sel = sample_selector_for_time(params->time);
862 abc_reader->read_geometry(geometry_set,
863 sample_sel,
864 params->read_flags,
865 params->velocity_name,
866 params->velocity_scale,
867 r_err_str);
868}
869
871 Object *ob,
872 const Mesh *existing_mesh,
873 const double time,
874 const char **r_err_str)
875{
876 AbcObjectReader *abc_reader = get_abc_reader(reader, ob, r_err_str);
877 if (abc_reader == nullptr) {
878 return false;
879 }
880
881 ISampleSelector sample_sel = sample_selector_for_time(time);
882 return abc_reader->topology_changed(existing_mesh, sample_sel);
883}
884
885/* ************************************************************************** */
886
888{
889 AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
890 abc_reader->decref();
891
892 if (abc_reader->refcount() == 0) {
893 delete abc_reader;
894 }
895}
896
898 CacheReader *reader,
899 Object *object,
900 const char *object_path,
901 const bool is_sequence)
902{
903 if (object_path[0] == '\0') {
904 return reader;
905 }
906
907 AlembicArchiveData *archive_data = archive_from_handle(handle);
908 if (!archive_data) {
909 return reader;
910 }
911
912 ArchiveReader *archive = archive_data->archive_reader;
913 if (!archive || !archive->valid()) {
914 return reader;
915 }
916
917 IObject iobject;
918 find_iobject(archive->getTop(), iobject, object_path);
919
920 if (reader) {
921 ABC_CacheReader_free(reader);
922 }
923
924 archive_data->settings->is_sequence = is_sequence;
927
928 AbcObjectReader *abc_reader = create_reader(iobject, *archive_data->settings);
929 if (abc_reader == nullptr) {
930 /* This object is not supported */
931 return nullptr;
932 }
933 abc_reader->object(object);
934 abc_reader->incref();
935
936 return reinterpret_cast<CacheReader *>(abc_reader);
937}
void * BKE_cachefile_add(Main *bmain, const char *name)
Definition cachefile.cc:311
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:366
General operations, lookup, etc. for blender objects.
void BKE_object_get_parent_matrix(const Object *ob, Object *par, float r_parentmat[4][4])
@ RPT_ERROR
Definition BKE_report.hh:39
#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:693
#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:1054
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1077
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_RECALC_BASE_FLAGS
Definition DNA_ID.h:1104
@ CACHEFILE_LAYER_HIDDEN
Object groups, one object can be in many groups at once.
Object is a sort of wrapper for general info.
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:98
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_ALEMBIC_IMPORT
Definition WM_api.hh:1796
@ WM_JOB_PROGRESS
Definition WM_api.hh:1766
#define NC_ID
Definition WM_types.hh:395
#define NC_SCENE
Definition WM_types.hh:378
#define NA_ADDED
Definition WM_types.hh:586
#define ND_FRAME
Definition WM_types.hh:434
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)
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()
@ ABC_ARCHIVE_FAIL
@ ABC_NO_ERROR
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)
nullptr float
#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:75
AbcObjectReader * create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings)
Definition abc_util.cc:155
void split(const std::string &s, const char delim, std::vector< std::string > &tokens)
Definition abc_util.cc:61
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_locked_interface_set(wmWindowManager *wm, bool lock)
void WM_global_report(eReportType type, const char *message)
void WM_main_add_notifier(uint type, void *reference)
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:376
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:479
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:211
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:388
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:360