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