1 //
2 // Copyright 2016-2019 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 /// \file alembicReader.cpp
25 
26 #include "pxr/pxr.h"
27 #include "pxr/usd/plugin/usdAbc/alembicReader.h"
28 #include "pxr/usd/plugin/usdAbc/alembicUtil.h"
29 #include "pxr/usd/usdGeom/hermiteCurves.h"
30 #include "pxr/usd/usdGeom/tokens.h"
31 #include "pxr/usd/usdGeom/xformable.h"
32 #include "pxr/usd/sdf/schema.h"
33 #include "pxr/base/work/threadLimits.h"
34 #include "pxr/base/trace/trace.h"
35 #include "pxr/base/tf/envSetting.h"
36 #include "pxr/base/tf/staticData.h"
37 #include "pxr/base/tf/staticTokens.h"
38 #include "pxr/base/tf/ostreamMethods.h"
39 #include <boost/type_traits/is_base_of.hpp>
40 #include <boost/utility/enable_if.hpp>
41 #include <Alembic/Abc/ArchiveInfo.h>
42 #include <Alembic/Abc/IArchive.h>
43 #include <Alembic/Abc/IObject.h>
44 #include <Alembic/Abc/ITypedArrayProperty.h>
45 #include <Alembic/Abc/ITypedScalarProperty.h>
46 #include <Alembic/AbcCoreAbstract/Foundation.h>
47 #include <Alembic/AbcCoreFactory/IFactory.h>
48 #include <Alembic/AbcGeom/GeometryScope.h>
49 #include <Alembic/AbcGeom/ICamera.h>
50 #include <Alembic/AbcGeom/ICurves.h>
51 #include <Alembic/AbcGeom/IPoints.h>
52 #include <Alembic/AbcGeom/IPolyMesh.h>
53 #include <Alembic/AbcGeom/ISubD.h>
54 #include <Alembic/AbcGeom/IXform.h>
55 #include <Alembic/AbcGeom/SchemaInfoDeclarations.h>
56 #include <Alembic/AbcGeom/Visibility.h>
57 #include <functional>
58 #include <memory>
59 #include <mutex>
60 
61 PXR_NAMESPACE_OPEN_SCOPE
62 
63 
64 // Define this to dump the namespace hierarchy as we traverse Alembic.
65 //#define USDABC_ALEMBIC_DEBUG
66 
67 TF_DEFINE_PRIVATE_TOKENS(
68     _tokens,
69     (transform)
70     ((xformOpTransform, "xformOp:transform"))
71 );
72 
73 TF_DEFINE_ENV_SETTING(
74     USD_ABC_WARN_ALL_UNSUPPORTED_VALUES, false,
75     "Issue warnings for all unsupported values encountered.");
76 
77 TF_DEFINE_ENV_SETTING(
78     USD_ABC_NUM_OGAWA_STREAMS, 1,
79     "The number of threads available for reading ogawa-backed files via UsdAbc.");
80 
81 TF_DEFINE_ENV_SETTING(
82     USD_ABC_WRITE_UV_AS_ST_TEXCOORD2FARRAY, true,
83     "Switch to false to disable writing Alembic uv sets as primvars:st with "
84     "type texCoord2fArray to USD");
85 
86 
87 TF_DEFINE_ENV_SETTING(
88     USD_ABC_XFORM_PRIM_COLLAPSE, true,
89     "Collapse Xforms containing a single geometry into a single geom Prim in USD");
90 
91 #if ALEMBIC_LIBRARY_VERSION >= 10709
92 TF_DEFINE_ENV_SETTING(
93     USD_ABC_READ_ARCHIVE_USE_MMAP, false,
94     "Use mmap when reading from an Ogawa archive.");
95 #endif
96 
97 namespace {
98 
99 using namespace ::Alembic::AbcGeom;
100 using namespace UsdAbc_AlembicUtil;
101 
102 static const TfToken&
_GetUVPropertyName()103 _GetUVPropertyName()
104 {
105     static const TfToken uvUsdAbcPropertyName =
106         (TfGetEnvSetting(USD_ABC_WRITE_UV_AS_ST_TEXCOORD2FARRAY)) ?
107         (UsdAbcPropertyNames->st) : (UsdAbcPropertyNames->uv);
108     return uvUsdAbcPropertyName;
109 }
110 
111 static const SdfValueTypeName&
_GetUVTypeName()112 _GetUVTypeName()
113 {
114     static const SdfValueTypeName uvTypeName =
115         (TfGetEnvSetting(USD_ABC_WRITE_UV_AS_ST_TEXCOORD2FARRAY)) ?
116         (SdfValueTypeNames->TexCoord2fArray) : (SdfValueTypeNames->Float2Array);
117     return uvTypeName;
118 }
119 
120 static size_t
_GetNumOgawaStreams()121 _GetNumOgawaStreams()
122 {
123     return std::min(TfGetEnvSetting(USD_ABC_NUM_OGAWA_STREAMS),
124                     static_cast<int>(WorkGetConcurrencyLimit()));
125 }
126 
127 #if PXR_HDF5_SUPPORT_ENABLED && !H5_HAVE_THREADSAFE
128 // A global mutex until our HDF5 library is thread safe.  It has to be
129 // recursive to handle the case where we write an Alembic file using an
130 // UsdAbc_AlembicData as the source.
131 static TfStaticData<std::recursive_mutex> _hdf5;
132 #endif // PXR_HDF5_SUPPORT_ENABLED
133 
134 // The SdfAbstractData time samples type.
135 // XXX: SdfAbstractData should typedef this.
136 typedef std::set<double> UsdAbc_TimeSamples;
137 
138 // A vector of Alembic times.
139 typedef std::vector<chrono_t> _AlembicTimeSamples;
140 
141 class _ReaderContext;
142 class _PrimReaderContext;
143 
144 //
145 // Error / warning message helpers
146 //
147 
148 // Helper function to produce a string representing the path
149 // to an Alembic property.
150 static std::string
_GetAlembicPath(const IScalarProperty & p)151 _GetAlembicPath(const IScalarProperty& p)
152 {
153     std::vector<std::string> names;
154     names.push_back(p.getName());
155 
156     for (ICompoundProperty prop = p.getParent();
157          prop.valid(); prop = prop.getParent()) {
158         names.push_back(prop.getName());
159     }
160 
161     const std::string propName = TfStringJoin(names.rbegin(), names.rend(),".");
162 
163     std::string path = p.getObject().getFullName();
164     if (!propName.empty() && propName[0] != '.') {
165         path += '.';
166     }
167     path += propName;
168 
169     return path;
170 }
171 
172 // Produce a string description of the sample selector
173 static std::string
_GetSampleSelectorDescription(const ISampleSelector & iss)174 _GetSampleSelectorDescription(const ISampleSelector& iss)
175 {
176     if (iss.getRequestedIndex() == -1) {
177         return "sample time " + TfStringify(iss.getRequestedTime());
178     }
179     return "sample index " + TfStringify(iss.getRequestedIndex());
180 }
181 
182 enum _WarningType
183 {
184     WarningVisibility = 0,
185     WarningSubdivisionScheme,
186     WarningInterpolateBoundary,
187     WarningFaceVaryingInterpolateBoundary
188 };
189 
190 static const char* _WarningNames[] =
191 {
192     "visibility",
193     "subdivision scheme",
194     "interpolate boundary",
195     "face varying interpolate boundary"
196 };
197 
198 static void
_PostUnsupportedValueWarning(const IScalarProperty & property,const ISampleSelector & iss,_WarningType warning,const std::string & authoredValue,const std::string & replacementValue)199 _PostUnsupportedValueWarning(
200     const IScalarProperty& property,
201     const ISampleSelector& iss,
202     _WarningType warning,
203     const std::string& authoredValue,
204     const std::string& replacementValue)
205 {
206     const IObject object = property.getObject();
207     const std::string archiveName = object.getArchive().getName();
208 
209     if (TfGetEnvSetting(USD_ABC_WARN_ALL_UNSUPPORTED_VALUES)) {
210         TF_WARN(
211             "Unsupported %s '%s' for <%s> at %s in archive '%s'. "
212             "Using '%s' instead.",
213             _WarningNames[warning],
214             authoredValue.c_str(),
215             _GetAlembicPath(property).c_str(),
216             _GetSampleSelectorDescription(iss).c_str(),
217             archiveName.c_str(),
218             replacementValue.c_str());
219         return;
220     }
221 
222     typedef std::pair<_WarningType, std::string> _WarningAndArchive;
223     typedef std::set<_WarningAndArchive> _IssuedWarnings;
224 
225     static _IssuedWarnings warnings;
226     static std::mutex mutex;
227 
228     bool issueWarning = false;
229     {
230         std::lock_guard<std::mutex> lock(mutex);
231         const _WarningAndArchive newWarning(warning, archiveName);
232         issueWarning = warnings.insert(newWarning).second;
233     }
234 
235     if (issueWarning) {
236         TF_WARN(
237             "Unsupported %s detected in archive '%s'. Using '%s' instead.",
238             _WarningNames[warning], archiveName.c_str(),
239             replacementValue.c_str());
240     }
241 }
242 
243 //
244 // Name helpers
245 //
246 
247 struct _AlembicFixName {
operator ()__anon67d290af0111::_AlembicFixName248     std::string operator()(std::string const &x) const {
249         return TfMakeValidIdentifier(x);
250     }
251 };
252 struct _AlembicFixNamespacedName {
operator ()__anon67d290af0111::_AlembicFixNamespacedName253     std::string operator()(std::string const &x) const {
254         auto elems = TfStringSplit(x, ":");
255         std::transform(elems.begin(), elems.end(), elems.begin(),
256                        TfMakeValidIdentifier);
257         return TfStringJoin(elems, ":");
258     }
259 };
260 
261 template <class T>
262 static
263 std::string
_CleanName(const std::string & inName,const char * trimLeading,const std::set<std::string> & usedNames,T fixer,bool (* test)(const std::string &)=& SdfPath::IsValidIdentifier)264 _CleanName(
265     const std::string& inName,
266     const char* trimLeading,
267     const std::set<std::string>& usedNames,
268     T fixer,
269     bool (*test)(const std::string&) = &SdfPath::IsValidIdentifier)
270 {
271     // Just return the name if it doesn't need mangling.  We assume the
272     // client has prepopulated usedNames with all Alembic names in the group.
273     if (test(inName)) {
274         return TfToken(inName);
275     }
276 
277     // Mangle name into desired form.
278 
279     // Handle empty name.
280     std::string name = inName;
281     if (name.empty()) {
282         name = '_';
283     }
284     else {
285         // Trim leading.
286         name = TfStringTrimLeft(name, trimLeading);
287 
288         // If name is not a valid identifier then substitute characters.
289         if (!test(name)) {
290             name = fixer(name);
291         }
292     }
293 
294     // Now check against usedNames.
295     if (usedNames.find(name) != usedNames.end()) {
296         // Just number the tries.
297         int i = 0;
298         std::string attempt = TfStringPrintf("%s_%d", name.c_str(), ++i);
299         while (usedNames.find(attempt) != usedNames.end()) {
300             attempt = TfStringPrintf("%s_%d", name.c_str(), ++i);
301         }
302         name = attempt;
303     }
304 
305     return name;
306 }
307 
308 //
309 // Metadata helpers
310 //
311 
312 // A map of metadata.
313 typedef std::map<TfToken, VtValue> MetadataMap;
314 
315 /// Returns the Alembic metadata name for a Usd metadata field name.
316 static
317 std::string
_AmdName(const std::string & name)318 _AmdName(const std::string& name)
319 {
320     return "Usd:" + name;
321 }
322 
323 static
324 void
_GetBoolMetadata(const MetaData & alembicMetadata,MetadataMap & usdMetadata,const TfToken & field)325 _GetBoolMetadata(
326     const MetaData& alembicMetadata,
327     MetadataMap& usdMetadata,
328     const TfToken& field)
329 {
330     std::string value = alembicMetadata.get(_AmdName(field));
331     if (!value.empty()) {
332         usdMetadata[field] = VtValue(value == "true");
333     }
334 }
335 
336 static
337 void
_GetStringMetadata(const MetaData & alembicMetadata,MetadataMap & usdMetadata,const TfToken & field)338 _GetStringMetadata(
339     const MetaData& alembicMetadata,
340     MetadataMap& usdMetadata,
341     const TfToken& field)
342 {
343     std::string value = alembicMetadata.get(_AmdName(field));
344     if (!value.empty()) {
345         usdMetadata[field] = VtValue(value);
346     }
347 }
348 
349 static
350 void
_GetTokenMetadata(const MetaData & alembicMetadata,MetadataMap & usdMetadata,const TfToken & field)351 _GetTokenMetadata(
352     const MetaData& alembicMetadata,
353     MetadataMap& usdMetadata,
354     const TfToken& field)
355 {
356     std::string value = alembicMetadata.get(_AmdName(field));
357     if (!value.empty()) {
358         usdMetadata[field] = VtValue(TfToken(value));
359     }
360 }
361 
362 static
363 void
_GetDoubleMetadata(const MetaData & alembicMetadata,MetadataMap & usdMetadata,const TfToken & field)364 _GetDoubleMetadata(
365     const MetaData& alembicMetadata,
366     MetadataMap& usdMetadata,
367     const TfToken& field)
368 {
369     std::string value = alembicMetadata.get(_AmdName(field));
370     if (!value.empty()) {
371         char* end;
372         const double v = strtod(value.c_str(), &end);
373         if (*end == '\0') {
374             usdMetadata[field] = VtValue(v);
375         }
376     }
377 }
378 
379 //
380 // AlembicProperty
381 //
382 
383 // Helpers for \c AlembicProperty.
384 template <class T>
385 struct _AlembicPropertyHelper {
386 //  T operator()(const ICompoundProperty& parent, const std::string& name)const;
387 };
388 template <>
389 struct _AlembicPropertyHelper<ICompoundProperty> {
390     ICompoundProperty
operator ()__anon67d290af0111::_AlembicPropertyHelper391     operator()(const ICompoundProperty& parent, const std::string& name,
392                SchemaInterpMatching matching = kStrictMatching) const
393     {
394         if (const PropertyHeader* header = parent.getPropertyHeader(name)) {
395             if (header->isCompound()) {
396                 return ICompoundProperty(parent, name);
397             }
398         }
399         return ICompoundProperty();
400     }
401 };
402 template <>
403 struct _AlembicPropertyHelper<IScalarProperty> {
404     IScalarProperty
operator ()__anon67d290af0111::_AlembicPropertyHelper405     operator()(const ICompoundProperty& parent, const std::string& name,
406                SchemaInterpMatching matching = kStrictMatching) const
407     {
408         if (const PropertyHeader* header = parent.getPropertyHeader(name)) {
409             if (header->isScalar()) {
410                 return IScalarProperty(parent, name);
411             }
412         }
413         return IScalarProperty();
414     }
415 };
416 template <class T>
417 struct _AlembicPropertyHelper<ITypedScalarProperty<T> > {
418     ITypedScalarProperty<T>
operator ()__anon67d290af0111::_AlembicPropertyHelper419     operator()(const ICompoundProperty& parent, const std::string& name,
420                SchemaInterpMatching matching = kStrictMatching) const
421     {
422         if (const PropertyHeader* header = parent.getPropertyHeader(name)) {
423             if (ITypedScalarProperty<T>::matches(*header, matching)) {
424                 return ITypedScalarProperty<T>(parent, name);
425             }
426         }
427         return ITypedScalarProperty<T>();
428     }
429 };
430 template <>
431 struct _AlembicPropertyHelper<IArrayProperty> {
432     IArrayProperty
operator ()__anon67d290af0111::_AlembicPropertyHelper433     operator()(const ICompoundProperty& parent, const std::string& name,
434                SchemaInterpMatching matching = kStrictMatching) const
435     {
436         if (const PropertyHeader* header = parent.getPropertyHeader(name)) {
437             if (header->isArray()) {
438                 return IArrayProperty(parent, name);
439             }
440         }
441         return IArrayProperty();
442     }
443 };
444 template <class T>
445 struct _AlembicPropertyHelper<ITypedArrayProperty<T> > {
446     ITypedArrayProperty<T>
operator ()__anon67d290af0111::_AlembicPropertyHelper447     operator()(const ICompoundProperty& parent, const std::string& name,
448                SchemaInterpMatching matching = kStrictMatching) const
449     {
450         if (const PropertyHeader* header = parent.getPropertyHeader(name)) {
451             if (ITypedArrayProperty<T>::matches(*header, matching)) {
452                 return ITypedArrayProperty<T>(parent, name);
453             }
454         }
455         return ITypedArrayProperty<T>();
456     }
457 };
458 template <class T>
459 struct _AlembicPropertyHelper<ITypedGeomParam<T> > {
460     ITypedGeomParam<T>
operator ()__anon67d290af0111::_AlembicPropertyHelper461     operator()(const ICompoundProperty& parent, const std::string& name,
462                SchemaInterpMatching matching = kStrictMatching) const
463     {
464         if (const PropertyHeader* header = parent.getPropertyHeader(name)) {
465             if (ITypedGeomParam<T>::matches(*header, matching)) {
466                 return ITypedGeomParam<T>(parent, name);
467             }
468         }
469         return ITypedGeomParam<T>();
470     }
471 };
472 
473 /// \class AlembicProperty
474 /// \brief Wraps an Alembic property of any type.
475 ///
476 /// An object of this type can hold any Alembic property but it must be
477 /// cast to get a concrete property object.  The client must know what
478 /// to cast to but the client can get the property header that describes
479 /// the held data.
480 class AlembicProperty {
481 public:
482     AlembicProperty(const SdfPath& path, const std::string& name);
483     AlembicProperty(const SdfPath& path, const std::string& name,
484                     const IObject& parent);
485     AlembicProperty(const SdfPath& path, const std::string& name,
486                     const ICompoundProperty& parent);
487 
488     /// Returns the parent compound property.
489     ICompoundProperty GetParent() const;
490 
491     /// Returns the name of the property.
492     const std::string& GetName() const;
493 
494     /// Get the property header.  This returns \c NULL if the property
495     /// doesn't exist.
496     const PropertyHeader* GetHeader() const;
497 
498     /// This is the only way to get an actual Alembic property object.
499     /// You must supply the expected type.  If it's incorrect then
500     /// you'll get an object of the requested type but its valid()
501     /// method will return \c false.
502     template <class T>
Cast(SchemaInterpMatching matching=kStrictMatching) const503     T Cast(SchemaInterpMatching matching = kStrictMatching) const
504     {
505         if (_parent.valid()) {
506             return _AlembicPropertyHelper<T>()(_parent, _name, matching);
507         }
508         else {
509             return T();
510         }
511     }
512 
513 private:
514     SdfPath _path;
515     ICompoundProperty _parent;
516     std::string _name;
517 };
518 
AlembicProperty(const SdfPath & path,const std::string & name)519 AlembicProperty::AlembicProperty(
520     const SdfPath& path,
521     const std::string& name) :
522     _path(path), _parent(), _name(name)
523 {
524     // Do nothing
525 }
526 
AlembicProperty(const SdfPath & path,const std::string & name,const IObject & parent)527 AlembicProperty::AlembicProperty(
528     const SdfPath& path,
529     const std::string& name,
530     const IObject& parent) :
531     _path(path), _parent(parent.getProperties()), _name(name)
532 {
533     // Do nothing
534 }
535 
AlembicProperty(const SdfPath & path,const std::string & name,const ICompoundProperty & parent)536 AlembicProperty::AlembicProperty(
537     const SdfPath& path,
538     const std::string& name,
539     const ICompoundProperty& parent) :
540     _path(path), _parent(parent), _name(name)
541 {
542     // Do nothing
543 }
544 
545 ICompoundProperty
GetParent() const546 AlembicProperty::GetParent() const
547 {
548     return _parent;
549 }
550 
551 const std::string&
GetName() const552 AlembicProperty::GetName() const
553 {
554     return _name;
555 }
556 
557 const PropertyHeader*
GetHeader() const558 AlembicProperty::GetHeader() const
559 {
560     return _parent.valid() ? _parent.getPropertyHeader(_name) : NULL;
561 }
562 
563 //
564 // _ReaderSchema
565 //
566 
567 /// \class _ReaderSchema
568 /// \brief The Usd to Alembic schema.
569 ///
570 /// This class stores functions to read a Usd prim from Alembic keyed by
571 /// type.  Each type can have multiple readers, each affected by the
572 /// previous via a \c _PrimReaderContext.
573 class _ReaderSchema {
574 public:
575     typedef std::function<void (_PrimReaderContext*)> PrimReader;
576     typedef std::vector<PrimReader> PrimReaderVector;
577     typedef UsdAbc_AlembicDataConversion::ToUsdConverter Converter;
578 
579     _ReaderSchema();
580 
581     /// Returns the prim readers for the given Alembic schema.  Returns an
582     /// empty vector if the schema isn't known.
583     const PrimReaderVector& GetPrimReaders(const std::string& schema) const;
584 
585     // Helper for defining types.
586     class TypeRef {
587     public:
TypeRef(PrimReaderVector * readers)588         TypeRef(PrimReaderVector* readers) : _readerVector(readers) { }
589 
AppendReader(const PrimReader & reader)590         TypeRef& AppendReader(const PrimReader& reader)
591         {
592             _readerVector->push_back(reader);
593             return *this;
594         }
595 
596     private:
597         PrimReaderVector* _readerVector;
598     };
599 
600     /// Adds a type and returns a helper for defining it.
601     template <class T>
AddType(T name)602     TypeRef AddType(T name)
603     {
604         return TypeRef(&_readers[name]);
605     }
606 
607     /// Adds the fallback type and returns a helper for defining it.
AddFallbackType()608     TypeRef AddFallbackType()
609     {
610         return AddType(std::string());
611     }
612 
613     /// Returns the object holding the conversions registry.
GetConversions() const614     const UsdAbc_AlembicDataConversion& GetConversions() const
615     {
616         return _conversions.data;
617     }
618 
619 private:
620     const UsdAbc_AlembicConversions _conversions;
621 
622     typedef std::map<std::string, PrimReaderVector> _ReaderMap;
623     _ReaderMap _readers;
624 };
625 
_ReaderSchema()626 _ReaderSchema::_ReaderSchema()
627 {
628     // Do nothing
629 }
630 
631 const _ReaderSchema::PrimReaderVector&
GetPrimReaders(const std::string & schema) const632 _ReaderSchema::GetPrimReaders(const std::string& schema) const
633 {
634     _ReaderMap::const_iterator i = _readers.find(schema);
635     if (i != _readers.end()) {
636         return i->second;
637     }
638     i = _readers.find(std::string());
639     if (i != _readers.end()) {
640         return i->second;
641     }
642     static const PrimReaderVector empty;
643     return empty;
644 }
645 
646 /// \class _ReaderContext
647 /// \brief The Alembic to Usd writer context.
648 ///
649 /// This object holds information used by the writer for a given archive
650 /// and Usd data.
651 class _ReaderContext {
652 public:
653     /// Gets data from some property at a given sample.
654     typedef std::function<bool (const UsdAbc_AlembicDataAny&,
655                                 const ISampleSelector&)> Converter;
656 
657     /// An optional ordering of name children or properties.
658     typedef boost::optional<TfTokenVector> Ordering;
659 
660     /// Sample times.
661     typedef UsdAbc_AlembicDataReader::TimeSamples TimeSamples;
662 
663     /// Sample index.
664     typedef UsdAbc_AlembicDataReader::Index Index;
665 
666     /// Property cache.
667     struct Property {
668         SdfValueTypeName typeName;
669         MetadataMap metadata;
670         TimeSamples sampleTimes;
671         bool timeSampled;
672         bool uniform;
673         Converter converter;
674     };
675     typedef std::map<TfToken, Property> PropertyMap;
676 
677     /// Prim cache.
678     struct Prim {
Prim__anon67d290af0111::_ReaderContext::Prim679         Prim() : instanceable(false), promoted(false) { }
680 
681         TfToken typeName;
682         TfTokenVector children;
683         TfTokenVector properties;
684         SdfSpecifier specifier;
685         Ordering primOrdering;
686         Ordering propertyOrdering;
687         MetadataMap metadata;
688         PropertyMap propertiesCache;
689         SdfPath prototype;           // Path to prototype; only set on instances
690         std::string instanceSource;  // Alembic path to instance source;
691                                      // only set on prototype.
692         bool instanceable;           // Instanceable; only set on prototype.
693         bool promoted;               // True if a promoted instance/prototype
694     };
695 
696     _ReaderContext();
697 
698     /// \name Reader setup
699     /// @{
700 
701     /// Open an archive.
702     bool Open(const std::string& filePath, std::string* errorLog,
703               const SdfFileFormat::FileFormatArguments& args);
704 
705     /// Close the archive.
706     void Close();
707 
708     /// Sets the reader schema.
SetSchema(const _ReaderSchema * schema)709     void SetSchema(const _ReaderSchema* schema) { _schema = schema; }
710 
711     /// Returns the reader schema.
GetSchema() const712     const _ReaderSchema& GetSchema() const { return *_schema; }
713 
714     /// Sets or resets the flag named \p flagName.
715     void SetFlag(const TfToken& flagName, bool set);
716 
717     /// @}
718     /// \name Reader caching
719     /// @{
720 
721     /// Returns \c true iff a flag is in the set.
722     bool IsFlagSet(const TfToken& flagName) const;
723 
724     /// Creates and returns the prim cache for path \p path.
725     Prim& AddPrim(const SdfPath& path);
726 
727     /// Returns \c true if \p object is an instance in Usd (i.e. it's an
728     /// instance in Alembic or is the source of an instance).
729     bool IsInstance(const IObject& object) const;
730 
731     /// Creates and returns the prim cache for path \p path that's an
732     /// instance of \p object.
733     Prim& AddInstance(const SdfPath& path, const IObject& object);
734 
735     /// Creates and returns the property cache for path \p path.
736     Property& FindOrCreateProperty(const SdfPath& path);
737 
738     /// Returns the property cache for path \p path if it exists, otherwise
739     /// \c NULL.
740     const Property* FindProperty(const SdfPath& path);
741 
742     /// Returns the sample times converted to Usd.
743     TimeSamples ConvertSampleTimes(const _AlembicTimeSamples&) const;
744 
745     /// Add the given sample times to the global set of sample times.
746     void AddSampleTimes(const TimeSamples&);
747 
748     /// @}
749     /// \name SdfAbstractData access
750     /// @{
751 
752     /// Test for the existence of a spec at \p id.
753     bool HasSpec(const SdfPath& path) const;
754 
755     /// Returns the spec type for the spec at \p id.
756     SdfSpecType GetSpecType(const SdfPath& path) const;
757 
758     /// Test for the existence of and optionally return the value at
759     /// (\p path,\p fieldName).
760     bool HasField(const SdfPath& path,
761                   const TfToken& fieldName,
762                   const UsdAbc_AlembicDataAny& value) const;
763 
764     /// Test for the existence of and optionally return the value of the
765     /// property at \p id at index \p index.
766     bool HasValue(const SdfPath& path, Index index,
767                   const UsdAbc_AlembicDataAny& value) const;
768 
769     /// Visit the specs.
770     void VisitSpecs(const SdfAbstractData& owner,
771                     SdfAbstractDataSpecVisitor* visitor) const;
772 
773     /// List the fields.
774     TfTokenVector List(const SdfPath& path) const;
775 
776     /// Returns the sampled times over all properties.
777     const UsdAbc_TimeSamples& ListAllTimeSamples() const;
778 
779     /// Returns the sampled times for the property with id \p id.
780     const TimeSamples&
781     ListTimeSamplesForPath(const SdfPath& path) const;
782 
783     /// @}
784 
785 private:
786     typedef AbcA::ObjectReaderPtr _ObjectPtr;
787     typedef std::set<_ObjectPtr> _ObjectReaderSet;
788     typedef std::map<_ObjectPtr, _ObjectReaderSet> _SourceToInstancesMap;
789 
790     // Walk the object hierarchy looking for instances and instance sources.
791     static void _FindInstances(const IObject& parent,
792                                _SourceToInstancesMap* instances);
793 
794     // Add to promotable those instance sources that are promotable.
795     static void _FindPromotable(const _SourceToInstancesMap& instances,
796                                 _ObjectReaderSet* promotable);
797 
798     // Store instancing state.
799     void _SetupInstancing(const _SourceToInstancesMap& instances,
800                           const _ObjectReaderSet& promotable,
801                           std::set<std::string>* usedNames);
802 
803     // Clear caches.
804     void _Clear();
805 
806     const Prim* _GetPrim(const SdfPath& path) const;
807     const Property* _GetProperty(const Prim&,
808                                  const SdfPath& path) const;
809     bool _HasField(const Prim* prim,
810                    const TfToken& fieldName,
811                    const UsdAbc_AlembicDataAny& value) const;
812     bool _HasField(const Property* property,
813                    const TfToken& fieldName,
814                    const UsdAbc_AlembicDataAny& value) const;
815     bool _HasValue(const Property* property,
816                    const ISampleSelector& selector,
817                    const UsdAbc_AlembicDataAny& value) const;
818 
819     // Custom auto-lock that safely ignores a NULL pointer.
820     class _Lock : boost::noncopyable {
821     public:
_Lock(std::recursive_mutex * mutex)822         _Lock(std::recursive_mutex* mutex) : _mutex(mutex) {
823             if (_mutex) _mutex->lock();
824         }
~_Lock()825         ~_Lock() { if (_mutex) _mutex->unlock(); }
826 
827     private:
828         std::recursive_mutex* _mutex;
829     };
830 
831 private:
832     // The mutex to lock when reading the archive.  This is NULL except
833     // for HDF5.
834     mutable std::recursive_mutex* _mutex;
835 
836     // Conversion options.
837     double _timeScale;              // Scale Alembic time by this factor.
838     double _timeOffset;             // Offset Alembic->Usd time (after scale).
839     std::set<TfToken, TfTokenFastArbitraryLessThan> _flags;
840 
841     // Input state.
842     IArchive _archive;
843     const _ReaderSchema* _schema;
844 
845     // A map of the full names of known instance sources.  The mapped
846     // value has the Usd prototype path and whether we're using the actual
847     // instance source or its parent.  We use the parent in some cases
848     // in order to get more sharing in the Usd world.
849     struct _PrototypeInfo {
850         SdfPath path;
851         bool promoted;
852     };
853     typedef std::map<std::string, _PrototypeInfo> _SourceToPrototypeMap;
854     _SourceToPrototypeMap _instanceSources;
855 
856     // A map of full name of instance to full name of source.  If we're
857     // using the parent of the source as the prototype then the source path
858     // is the parent of the actual source and the instance path is the
859     // parent of the actual instance.
860     typedef std::map<std::string, std::string> _InstanceToSourceMap;
861     _InstanceToSourceMap _instances;
862 
863     // Caches.
864     typedef std::map<SdfPath, Prim> _PrimMap;
865     _PrimMap _prims;
866     Prim* _pseudoRoot;
867     UsdAbc_TimeSamples _allTimeSamples;
868 };
869 
870 static
871 void
872 _ReadPrimChildren(
873     _ReaderContext& context,
874     const IObject& object,
875     const SdfPath& path,
876     _ReaderContext::Prim& prim);
877 
_ReaderContext()878 _ReaderContext::_ReaderContext() :
879     _mutex(NULL),
880     _timeScale(24.0),               // Usd is frames, Alembic is seconds.
881     _timeOffset(0.0),               // Time 0.0 to frame 0.
882     _schema(NULL)
883 {
884     // Do nothing
885 }
886 
887 bool
Open(const std::string & filePath,std::string * errorLog,const SdfFileFormat::FileFormatArguments & args)888 _ReaderContext::Open(const std::string& filePath, std::string* errorLog,
889                      const SdfFileFormat::FileFormatArguments& args)
890 {
891     Close();
892 
893     std::vector<std::string> layeredABC;
894     {
895         auto abcLayers = args.find("abcLayers");
896         if (abcLayers != args.end()) {
897             for (auto&& l : TfStringSplit(abcLayers->second, ",")) {
898                 layeredABC.emplace_back(std::move(l));
899             }
900         }
901     }
902     layeredABC.emplace_back(filePath);
903 
904 #if PXR_HDF5_SUPPORT_ENABLED && !H5_HAVE_THREADSAFE
905     // HDF5 may not be thread-safe.
906     using lock_guard = std::lock_guard<std::recursive_mutex>;
907     std::unique_ptr<std::lock_guard<std::recursive_mutex>> hfd5Lock(new lock_guard(*_hdf5));
908 #endif
909 
910     using IFactory = ::Alembic::AbcCoreFactory::IFactory;
911     IFactory factory;
912     IFactory::CoreType abcType;
913     factory.setPolicy(Abc::ErrorHandler::Policy::kQuietNoopPolicy);
914     factory.setOgawaNumStreams(_GetNumOgawaStreams());
915     IArchive archive = factory.getArchive(layeredABC, abcType);
916 
917 #if PXR_HDF5_SUPPORT_ENABLED && !H5_HAVE_THREADSAFE
918     if (abcType == IFactory::kHDF5 || abcType == IFactory::kLayer) {
919         // An HDF5, or layered which may have an HDF5 layer
920         _mutex = &*_hdf5;
921     } else {
922         // Don't need the HDF5 lock
923         hfd5Lock.reset();
924     }
925 #endif
926 
927     std::string format;
928     switch (abcType) {
929         case IFactory::kHDF5: format = "HDF5"; break;
930         case IFactory::kOgawa: format = "Ogawa"; break;
931         case IFactory::kLayer: format = "Layer"; break;
932         default:
933         case IFactory::kUnknown: format = "Unknown"; break;
934     }
935     if (!archive.valid()) {
936         *errorLog = TfStringPrintf("Unsupported format: '%s'", format.c_str());
937         return false;
938     }
939 
940     // Get info.
941     uint32_t apiVersion;
942     std::string writer, version, date, comment;
943     GetArchiveInfo(archive, writer, version, apiVersion, date, comment);
944 
945     // Report.
946     if (IsFlagSet(UsdAbc_AlembicContextFlagNames->verbose)) {
947         TF_STATUS("Opened %s file written by Alembic %s",
948                   format.c_str(),
949                   UsdAbc_FormatAlembicVersion(apiVersion).c_str());
950     }
951 
952     // Cut over.
953     _archive = std::move(archive);
954 
955     // Fill pseudo-root in the cache.
956     const SdfPath rootPath = SdfPath::AbsoluteRootPath();
957     _pseudoRoot = &_prims[rootPath];
958     _pseudoRoot->metadata[SdfFieldKeys->Documentation] = comment;
959 
960     // Gather the names of the root prims.  Instancing will want to create
961     // new root prims.  Those must have unique names that don't modify the
962     // names of existing root prims.  So we have to have the existing names
963     // first.
964     IObject root = _archive.getTop();
965     std::set<std::string> usedRootNames;
966     for (size_t i = 0, n = root.getNumChildren(); i != n; ++i) {
967         IObject child(root, root.getChildHeader(i).getName());
968         std::string name = _CleanName(child.getName(), " _", usedRootNames,
969                                       _AlembicFixName(),
970                                       &SdfPath::IsValidIdentifier);
971         usedRootNames.insert(name);
972     }
973 
974     // Fetch authored timeCodesPerSecond early so that we can use it
975     // for rescaling timeSamples.
976     if (const PropertyHeader* property =
977             root.getProperties().getPropertyHeader("Usd")) {
978         const MetaData& metadata = property->getMetaData();
979         _pseudoRoot->metadata[SdfFieldKeys->TimeCodesPerSecond] = 24.0;
980        _GetDoubleMetadata(metadata, _pseudoRoot->metadata,
981                            SdfFieldKeys->TimeCodesPerSecond);
982        _timeScale = _pseudoRoot->metadata[SdfFieldKeys->TimeCodesPerSecond].Get<double>();
983     }
984 
985     // Collect instancing information.
986     // Skipping this step makes later code expand instances.
987     if (!IsFlagSet(UsdAbc_AlembicContextFlagNames->expandInstances)) {
988         // Find the instance sources and their instances.
989         _SourceToInstancesMap instances;
990         _FindInstances(root, &instances);
991 
992         // If we're allowing instance promotion find the candidates.
993         _ObjectReaderSet promotable;
994         if (IsFlagSet(UsdAbc_AlembicContextFlagNames->promoteInstances)) {
995             _FindPromotable(instances, &promotable);
996         }
997 
998         // Save instancing info to lookup during main traversal, including
999         // choosing paths for the prototypes.
1000         _SetupInstancing(instances, promotable, &usedRootNames);
1001     }
1002 
1003     // Re-root so the <defaultPrim> is actually the archive!
1004     TfToken abcReRoot;
1005     {
1006         auto reRoot = args.find("abcReRoot");
1007         if (reRoot != args.end()) {
1008             if (!TfIsValidIdentifier(reRoot->second)) {
1009                 TF_WARN("[usdAbc] Ignoring re-root because identifer '%s' is"
1010                         " not valid (%s).", reRoot->second.c_str(),
1011                         filePath.c_str());
1012             } else
1013                 abcReRoot = TfToken(reRoot->second);
1014         }
1015     }
1016 
1017     // Fill rest of the cache.
1018     if (!abcReRoot.IsEmpty()) {
1019         SdfPath compPath = rootPath.AppendChild(abcReRoot);
1020         auto& xform = _prims[compPath];
1021         xform.typeName = UsdAbcPrimTypeNames->Xform;
1022         xform.specifier = SdfSpecifierDef;
1023         _ReadPrimChildren(*this, root, compPath, xform);
1024         _pseudoRoot->children.emplace_back(std::move(abcReRoot));
1025     } else
1026         _ReadPrimChildren(*this, root, rootPath, *_pseudoRoot);
1027 
1028     // Append the prototypes to the pseudo-root.  We use lexicographical order
1029     // but the order doesn't really matter.  We also note here the Alembic
1030     // source path for each prototype and whether it's instanceable or not
1031     // (which is chosen simply by the disableInstancing flag).
1032     if (!_instanceSources.empty()) {
1033         const bool instanceable =
1034             !IsFlagSet(UsdAbc_AlembicContextFlagNames->disableInstancing);
1035         std::map<SdfPath, std::string> prototypes;
1036         for (const _SourceToPrototypeMap::value_type& v: _instanceSources) {
1037             prototypes[v.second.path] = v.first;
1038         }
1039         for (const auto& prototype: prototypes) {
1040             const SdfPath& name = prototype.first;
1041             _pseudoRoot->children.push_back(name.GetNameToken());
1042             _prims[name].instanceSource = prototype.second;
1043             _prims[name].instanceable   = instanceable;
1044         }
1045     }
1046 
1047     // Guess the start and end timeCode using the sample times.
1048     if (!_allTimeSamples.empty()) {
1049         _pseudoRoot->metadata[SdfFieldKeys->StartTimeCode] =
1050             *_allTimeSamples.begin();
1051         _pseudoRoot->metadata[SdfFieldKeys->EndTimeCode]   =
1052             *_allTimeSamples.rbegin();
1053     }
1054 
1055     // If no upAxis is authored, pretend that it was authored as 'Y'.  This
1056     // is primarily to facilitate working with externally-generated abc
1057     // files at Pixar, where we unfortunately still work in a Z-up pipeline,
1058     // and therefore configure UsdGeomGetFallbackUpAxis() to return 'Z'.
1059     _pseudoRoot->metadata[UsdGeomTokens->upAxis] = UsdGeomTokens->y;
1060 
1061     // Get the Usd metadata.  This will overwrite any metadata previously
1062     // set on _pseudoRoot.
1063     if (const PropertyHeader* property =
1064             root.getProperties().getPropertyHeader("Usd")) {
1065         const MetaData& metadata = property->getMetaData();
1066         _GetDoubleMetadata(metadata, _pseudoRoot->metadata,
1067                            SdfFieldKeys->StartTimeCode);
1068         _GetDoubleMetadata(metadata, _pseudoRoot->metadata,
1069                            SdfFieldKeys->EndTimeCode);
1070 
1071         _GetDoubleMetadata(metadata, _pseudoRoot->metadata,
1072                            SdfFieldKeys->FramesPerSecond);
1073 
1074         _GetTokenMetadata(metadata, _pseudoRoot->metadata,
1075                           UsdGeomTokens->upAxis);
1076 
1077         // Read the default prim name.
1078         _GetTokenMetadata(metadata, _pseudoRoot->metadata,
1079                           SdfFieldKeys->DefaultPrim);
1080     }
1081 
1082     // If no default prim then choose one by a heuristic (first root prim).
1083     if (!_pseudoRoot->children.empty()) {
1084         // Use emplace to leave existing property above untouched and avoid the
1085         // VtValue construction if possible (gcc-4.8 requires the fun syntax)
1086         _pseudoRoot->metadata.emplace(std::piecewise_construct,
1087             std::forward_as_tuple(SdfFieldKeys->DefaultPrim),
1088             std::forward_as_tuple(_pseudoRoot->children.front()));
1089     }
1090 
1091     return true;
1092 }
1093 
1094 void
Close()1095 _ReaderContext::Close()
1096 {
1097     _Clear();
1098 
1099     _Lock lock(_mutex);
1100     _archive = IArchive();
1101     _mutex = NULL;
1102 }
1103 
1104 void
SetFlag(const TfToken & flagName,bool set)1105 _ReaderContext::SetFlag(const TfToken& flagName, bool set)
1106 {
1107     if (set) {
1108         _flags.insert(flagName);
1109     }
1110     else {
1111         _flags.erase(flagName);
1112     }
1113 }
1114 
1115 bool
IsFlagSet(const TfToken & flagName) const1116 _ReaderContext::IsFlagSet(const TfToken& flagName) const
1117 {
1118     return _flags.count(flagName);
1119 }
1120 
1121 _ReaderContext::Prim&
AddPrim(const SdfPath & path)1122 _ReaderContext::AddPrim(const SdfPath& path)
1123 {
1124     return _prims[path];
1125 }
1126 
1127 bool
IsInstance(const IObject & object) const1128 _ReaderContext::IsInstance(const IObject& object) const
1129 {
1130     // object is an instance if it's a key in _instances.
1131     return _instances.find(object.getFullName()) != _instances.end();
1132 }
1133 
1134 _ReaderContext::Prim&
AddInstance(const SdfPath & path,const IObject & object)1135 _ReaderContext::AddInstance(const SdfPath& path, const IObject& object)
1136 {
1137     // An instance is just a prim...
1138     Prim& result = AddPrim(path);
1139 
1140     // ...that also has its prototype member set.
1141     const auto i = _instances.find(object.getFullName());
1142     if (i != _instances.end()) {
1143         const _PrototypeInfo& info = _instanceSources.find(i->second)->second;
1144         result.prototype   = info.path;
1145         result.promoted = info.promoted;
1146     }
1147     return result;
1148 }
1149 
1150 _ReaderContext::Property&
FindOrCreateProperty(const SdfPath & path)1151 _ReaderContext::FindOrCreateProperty(const SdfPath& path)
1152 {
1153     return AddPrim(path.GetPrimPath()).propertiesCache[path.GetNameToken()];
1154 }
1155 
1156 const _ReaderContext::Property*
FindProperty(const SdfPath & path)1157 _ReaderContext::FindProperty(const SdfPath& path)
1158 {
1159     _PrimMap::const_iterator i = _prims.find(path.GetPrimPath());
1160     if (i != _prims.end()) {
1161         PropertyMap::const_iterator j =
1162             i->second.propertiesCache.find(path.GetNameToken());
1163         if (j != i->second.propertiesCache.end()) {
1164             return &j->second;
1165         }
1166     }
1167     return NULL;
1168 }
1169 
1170 _ReaderContext::TimeSamples
ConvertSampleTimes(const _AlembicTimeSamples & alembicTimes) const1171 _ReaderContext::ConvertSampleTimes(
1172     const _AlembicTimeSamples& alembicTimes) const
1173 {
1174     std::vector<double> result;
1175     result.resize(alembicTimes.size());
1176 
1177     // Special case.
1178     if (_timeScale == 1.0 && _timeOffset == 0.0) {
1179         std::copy(alembicTimes.begin(), alembicTimes.end(), result.begin());
1180     }
1181     else {
1182         for (size_t i = 0, n = result.size(); i != n; ++i) {
1183             // Round the result so we get exact frames in the common
1184             // case of times stored in seconds and _timeScale = 1/24.
1185             static const double P = 1.0e+10;
1186             result[i] =
1187                 GfRound(P * (alembicTimes[i] * _timeScale + _timeOffset)) / P;
1188         }
1189     }
1190 
1191     return TimeSamples(result);
1192 }
1193 
1194 void
AddSampleTimes(const TimeSamples & sampleTimes)1195 _ReaderContext::AddSampleTimes(const TimeSamples& sampleTimes)
1196 {
1197     sampleTimes.AddTo(&_allTimeSamples);
1198 }
1199 
1200 bool
HasSpec(const SdfPath & path) const1201 _ReaderContext::HasSpec(const SdfPath& path) const
1202 {
1203     if (const Prim* prim = _GetPrim(path)) {
1204         return path.IsAbsoluteRootOrPrimPath() || _GetProperty(*prim, path);
1205     }
1206     return false;
1207 }
1208 
1209 SdfSpecType
GetSpecType(const SdfPath & path) const1210 _ReaderContext::GetSpecType(const SdfPath& path) const
1211 {
1212     if (const Prim* prim = _GetPrim(path)) {
1213         if (!path.IsAbsoluteRootOrPrimPath()) {
1214             if (_GetProperty(*prim, path)) {
1215                 return SdfSpecTypeAttribute;
1216             }
1217         }
1218         else if (prim == _pseudoRoot) {
1219             return SdfSpecTypePseudoRoot;
1220         }
1221         else {
1222             return SdfSpecTypePrim;
1223         }
1224     }
1225     return SdfSpecTypeUnknown;
1226 }
1227 
1228 bool
HasField(const SdfPath & path,const TfToken & fieldName,const UsdAbc_AlembicDataAny & value) const1229 _ReaderContext::HasField(
1230     const SdfPath& path,
1231     const TfToken& fieldName,
1232     const UsdAbc_AlembicDataAny& value) const
1233 {
1234     TRACE_FUNCTION();
1235 
1236     if (const Prim* prim = _GetPrim(path)) {
1237         if (!path.IsAbsoluteRootOrPrimPath()) {
1238             if (const Property* property = _GetProperty(*prim, path)) {
1239                 return _HasField(property, fieldName, value);
1240             }
1241         }
1242         else {
1243             return _HasField(prim, fieldName, value);
1244         }
1245     }
1246     return false;
1247 }
1248 
1249 bool
HasValue(const SdfPath & path,Index index,const UsdAbc_AlembicDataAny & value) const1250 _ReaderContext::HasValue(
1251     const SdfPath& path,
1252     Index index,
1253     const UsdAbc_AlembicDataAny& value) const
1254 {
1255     TRACE_FUNCTION();
1256 
1257     if (const Prim* prim = _GetPrim(path)) {
1258         if (!path.IsAbsoluteRootOrPrimPath()) {
1259             if (const Property* property = _GetProperty(*prim, path)) {
1260                 return _HasValue(property, ISampleSelector(index), value);
1261             }
1262         }
1263     }
1264     return false;
1265 }
1266 
1267 void
VisitSpecs(const SdfAbstractData & owner,SdfAbstractDataSpecVisitor * visitor) const1268 _ReaderContext::VisitSpecs(
1269     const SdfAbstractData& owner,
1270     SdfAbstractDataSpecVisitor* visitor) const
1271 {
1272     // Visit prims in path sorted order.
1273     for (const auto& v : _prims) {
1274         // Visit the prim.
1275         const SdfPath& primPath = v.first;
1276         if (!visitor->VisitSpec(owner, primPath)) {
1277             return;
1278         }
1279 
1280         // Visit the prim's properties in lexicographical sorted order.
1281         const Prim& prim = v.second;
1282         if (&prim != _pseudoRoot) {
1283             for (const auto& w : prim.propertiesCache) {
1284                 if (!visitor->VisitSpec(owner,
1285                                         primPath.AppendProperty(w.first))) {
1286                     return;
1287                 }
1288             }
1289         }
1290     }
1291 }
1292 
1293 TfTokenVector
List(const SdfPath & path) const1294 _ReaderContext::List(const SdfPath& path) const
1295 {
1296     TRACE_FUNCTION();
1297 
1298     TfTokenVector result;
1299 
1300     if (const Prim* prim = _GetPrim(path)) {
1301         if (!path.IsAbsoluteRootOrPrimPath()) {
1302             if (const Property* property = _GetProperty(*prim, path)) {
1303                 result.push_back(SdfFieldKeys->TypeName);
1304                 result.push_back(SdfFieldKeys->Custom);
1305                 result.push_back(SdfFieldKeys->Variability);
1306                 if (property->timeSampled) {
1307                     result.push_back(SdfFieldKeys->TimeSamples);
1308                 }
1309                 else if (!property->sampleTimes.IsEmpty()) {
1310                     result.push_back(SdfFieldKeys->Default);
1311                 }
1312 
1313                 // Add metadata.
1314                 for (const auto& v : property->metadata) {
1315                     result.push_back(v.first);
1316                 }
1317             }
1318         }
1319         else {
1320             if (prim != _pseudoRoot) {
1321                 if (!prim->typeName.IsEmpty()) {
1322                     result.push_back(SdfFieldKeys->TypeName);
1323                 }
1324                 result.push_back(SdfFieldKeys->Specifier);
1325                 if (!prim->properties.empty()) {
1326                     result.push_back(SdfChildrenKeys->PropertyChildren);
1327                 }
1328                 if (prim->primOrdering) {
1329                     result.push_back(SdfFieldKeys->PrimOrder);
1330                 }
1331                 if (prim->propertyOrdering) {
1332                     result.push_back(SdfFieldKeys->PropertyOrder);
1333                 }
1334                 if (!prim->prototype.IsEmpty()) {
1335                     result.push_back(SdfFieldKeys->References);
1336                 }
1337                 if (!prim->instanceSource.empty()) {
1338                     result.push_back(SdfFieldKeys->CustomData);
1339                 }
1340                 if (prim->instanceable && !prim->instanceSource.empty()) {
1341                     result.push_back(SdfFieldKeys->Instanceable);
1342                 }
1343             }
1344             if (!prim->children.empty()) {
1345                 result.push_back(SdfChildrenKeys->PrimChildren);
1346             }
1347             for (const auto& v : prim->metadata) {
1348                 result.push_back(v.first);
1349             }
1350         }
1351     }
1352 
1353     return result;
1354 }
1355 
1356 const std::set<double>&
ListAllTimeSamples() const1357 _ReaderContext::ListAllTimeSamples() const
1358 {
1359     return _allTimeSamples;
1360 }
1361 
1362 const _ReaderContext::TimeSamples&
ListTimeSamplesForPath(const SdfPath & path) const1363 _ReaderContext::ListTimeSamplesForPath(const SdfPath& path) const
1364 {
1365     TRACE_FUNCTION();
1366 
1367     if (path.IsPropertyPath()) {
1368         if (const Prim* prim = _GetPrim(path)) {
1369             if (const Property* property = _GetProperty(*prim, path)) {
1370                 if (property->timeSampled) {
1371                     return property->sampleTimes;
1372                 }
1373             }
1374         }
1375     }
1376 
1377     static const TimeSamples empty;
1378     return empty;
1379 }
1380 
1381 void
_FindInstances(const IObject & parent,_SourceToInstancesMap * instances)1382 _ReaderContext::_FindInstances(
1383     const IObject& parent,
1384     _SourceToInstancesMap* instances)
1385 {
1386     for (size_t i = 0, n = parent.getNumChildren(); i != n; ++i) {
1387         IObject child(parent, parent.getChildHeader(i).getName());
1388         if (child.isInstanceRoot()) {
1389             // child is the top prim of an instance.
1390             (*instances)[child.getPtr()].insert(child.getInstancePtr());
1391         }
1392         else {
1393             // Descend the hierarchy outside of instance roots.  We can
1394             // terminate the recursion at the top of an instance's root
1395             // since there's nothing under there we won't see when we
1396             // traverse (or traversed) the instance source.
1397             _FindInstances(child, instances);
1398         }
1399     }
1400 }
1401 
1402 void
_FindPromotable(const _SourceToInstancesMap & instances,_ObjectReaderSet * promotable)1403 _ReaderContext::_FindPromotable(
1404     const _SourceToInstancesMap& instances,
1405     _ObjectReaderSet* promotable)
1406 {
1407     // We want to use the parent of the source (and the parents of the
1408     // corresponging instances) where possible.  Since Usd can't share
1409     // the prototype prim but can share its descendants, we can get better
1410     // sharing when we can use the parent.  We can't do this if the
1411     // source or any instance has siblings and we won't do this unless
1412     // the source/instance is an IGeomBase and its parent is a transform.
1413     //
1414     // If we can use the parent we say the source is promotable.
1415     for (const auto& value: instances) {
1416         const _ObjectPtr& source = value.first;
1417         if (source->getParent()->getNumChildren() != 1) {
1418             // Source has siblings.
1419             continue;
1420         }
1421 
1422         // Source must be an IGeomBase and its parent must be an IXform.
1423         if (!IGeomBase::matches(source->getMetaData())) {
1424             continue;
1425         }
1426         if (!IXform::matches(source->getParent()->getMetaData())) {
1427             continue;
1428         }
1429 
1430         // Check instances for siblings.
1431         for (const auto& instance: value.second) {
1432             if (instance->getParent()->getNumChildren() != 1) {
1433                 // Instance has siblings.
1434                 continue;
1435             }
1436         }
1437 
1438         // We can promote this source.
1439         promotable->insert(source);
1440     }
1441 }
1442 
1443 void
_SetupInstancing(const _SourceToInstancesMap & instances,const _ObjectReaderSet & promotable,std::set<std::string> * usedNames)1444 _ReaderContext::_SetupInstancing(
1445     const _SourceToInstancesMap& instances,
1446     const _ObjectReaderSet& promotable,
1447     std::set<std::string>* usedNames)
1448 {
1449     // Now build the mapping of instances to sources and a mapping from the
1450     // (possibly promoted) source full name to the corresponding Usd prototype
1451     // prim path.  We can no longer use Alembic to answer these questions
1452     // since it doesn't know about promoted prototypes/instances.
1453     for (const auto& value: instances) {
1454         const _ObjectPtr& source = value.first;
1455         const bool promoted = (promotable.find(source) != promotable.end());
1456         const std::string& sourceFullName = promoted
1457             ? source->getParent()->getFullName()
1458             : source->getFullName();
1459         if (promoted) {
1460             for (const auto& instance: value.second) {
1461                 _instances[instance->getParent()->getFullName()] =
1462                     sourceFullName;
1463             }
1464         }
1465         else {
1466             // Not promotable.
1467             for (const auto& instance: value.second) {
1468                 _instances[instance->getFullName()] = sourceFullName;
1469             }
1470         }
1471 
1472         // The Alembic instance source is just another instance as far
1473         // as Usd is concerned.  Unlike Alembic, Usd creates a separate
1474         // prototype that has special treatment.
1475         _instances[sourceFullName] = sourceFullName;
1476 
1477         // Construct a unique name.  Start by getting the name
1478         // portion of the instance source's path.
1479         const std::string::size_type j = sourceFullName.rfind('/');
1480         const std::string prototypeName =
1481             (j != std::string::npos)
1482                 ? sourceFullName.substr(j + 1)
1483                 : sourceFullName;
1484 
1485         // Now mangle/uniquify the name and make a root prim path.
1486         const SdfPath prototypePath =
1487                 SdfPath::AbsoluteRootPath().AppendChild(TfToken(
1488                     _CleanName(prototypeName, " _", *usedNames,
1489                                _AlembicFixName(),
1490                                &SdfPath::IsValidIdentifier)));
1491 
1492         // Save the source/prototype info.
1493         _PrototypeInfo prototypeInfo = { prototypePath, promoted };
1494         _instanceSources.emplace(sourceFullName, std::move(prototypeInfo));
1495     }
1496 }
1497 
1498 void
_Clear()1499 _ReaderContext::_Clear()
1500 {
1501     _prims.clear();
1502     _pseudoRoot = NULL;
1503     _allTimeSamples.clear();
1504     _instanceSources.clear();
1505     _instances.clear();
1506 }
1507 
1508 const _ReaderContext::Prim*
_GetPrim(const SdfPath & path) const1509 _ReaderContext::_GetPrim(const SdfPath& path) const
1510 {
1511     _PrimMap::const_iterator i = _prims.find(path.GetAbsoluteRootOrPrimPath());
1512     return i == _prims.end() ? NULL : &i->second;
1513 }
1514 
1515 const _ReaderContext::Property*
_GetProperty(const Prim & prim,const SdfPath & path) const1516 _ReaderContext::_GetProperty(
1517     const Prim& prim,
1518     const SdfPath& path) const
1519 {
1520     // The alembic reader does not support relational attributes; only prim
1521     // properties.
1522     if (!path.IsPrimPropertyPath()) {
1523         return nullptr;
1524     }
1525     PropertyMap::const_iterator i =
1526         prim.propertiesCache.find(path.GetNameToken());
1527     return i == prim.propertiesCache.end() ? NULL : &i->second;
1528 }
1529 
1530 bool
_HasField(const Prim * prim,const TfToken & fieldName,const UsdAbc_AlembicDataAny & value) const1531 _ReaderContext::_HasField(
1532     const Prim* prim,
1533     const TfToken& fieldName,
1534     const UsdAbc_AlembicDataAny& value) const
1535 {
1536     if (fieldName == SdfChildrenKeys->PrimChildren) {
1537         if (!prim->children.empty()) {
1538             return value.Set(prim->children);
1539         }
1540     }
1541 
1542     if (prim != _pseudoRoot) {
1543         if (fieldName == SdfFieldKeys->TypeName) {
1544             return value.Set(prim->typeName);
1545         }
1546         else if (fieldName == SdfFieldKeys->PrimOrder) {
1547             if (prim->primOrdering) {
1548                 return value.Set(*prim->primOrdering);
1549             }
1550         }
1551         else if (fieldName == SdfFieldKeys->PropertyOrder) {
1552             if (prim->propertyOrdering) {
1553                 return value.Set(*prim->propertyOrdering);
1554             }
1555         }
1556         else if (fieldName == SdfFieldKeys->Specifier) {
1557             return value.Set(prim->specifier);
1558         }
1559         else if (fieldName == SdfChildrenKeys->PropertyChildren) {
1560             if (!prim->properties.empty()) {
1561                 return value.Set(prim->properties);
1562             }
1563         }
1564         else if (fieldName == SdfFieldKeys->CustomData) {
1565             // Provide the Alembic source path on prototype prims.  In Usd
1566             // we copy the instance source to a new root prototype prim and
1567             // the instance source becomes just another instance of the
1568             // prototype.  This gives us a breadcrumb to follow back.
1569             if (!prim->instanceSource.empty()) {
1570                 static const std::string key("abcInstanceSourcePath");
1571                 VtDictionary data;
1572                 data[key] = VtValue(prim->instanceSource);
1573                 return value.Set(data);
1574             }
1575         }
1576         else if (fieldName == SdfFieldKeys->Instanceable) {
1577             if (!prim->instanceSource.empty()) {
1578                 return value.Set(prim->instanceable);
1579             }
1580         }
1581         else if (fieldName == SdfFieldKeys->References) {
1582             if (!prim->prototype.IsEmpty()) {
1583                 SdfReferenceListOp refs;
1584                 SdfReferenceVector items;
1585                 items.push_back(SdfReference(std::string(), prim->prototype));
1586                 refs.SetExplicitItems(items);
1587                 return value.Set(refs);
1588             }
1589         }
1590     }
1591 
1592     TRACE_SCOPE("UsdAbc_AlembicDataReader::_HasField:OtherMetadata");
1593     MetadataMap::const_iterator j = prim->metadata.find(fieldName);
1594     if (j != prim->metadata.end()) {
1595         return value.Set(j->second);
1596     }
1597 
1598     return false;
1599 }
1600 
1601 bool
_HasField(const Property * property,const TfToken & fieldName,const UsdAbc_AlembicDataAny & value) const1602 _ReaderContext::_HasField(
1603     const Property* property,
1604     const TfToken& fieldName,
1605     const UsdAbc_AlembicDataAny& value) const
1606 {
1607     if (fieldName == SdfFieldKeys->Default) {
1608         // No default value if we're time sampled.  Alembic does not
1609         // distinguish default and time samples so we either have one
1610         // sample (the default) or more than one sample (time sampled).
1611         if (!property->timeSampled && !property->sampleTimes.IsEmpty()){
1612             return _HasValue(property, ISampleSelector(), value);
1613         }
1614     }
1615     else if (fieldName == SdfFieldKeys->TimeSamples) {
1616         if (property->timeSampled) {
1617             if (value) {
1618                 TRACE_SCOPE("UsdAbc_AlembicDataReader::_HasField:TimeSamples");
1619                 // Fill a map of values over all time samples.
1620                 VtValue tmp;
1621                 UsdAbc_AlembicDataAny any(&tmp);
1622                 SdfTimeSampleMap samples;
1623                 const Index n = property->sampleTimes.GetSize();
1624                 for (Index j = 0; j != n; ++j) {
1625                     if (_HasValue(property, ISampleSelector(j), any)) {
1626                         samples[property->sampleTimes[j]] = tmp;
1627                     }
1628                 }
1629                 return value.Set(samples);
1630             }
1631             else {
1632                 return true;
1633             }
1634         }
1635     }
1636     else if (fieldName == SdfFieldKeys->TypeName) {
1637         return value.Set(property->typeName.GetAsToken());
1638     }
1639     else if (fieldName == SdfFieldKeys->Variability) {
1640         return value.Set(property->uniform ?
1641                          SdfVariabilityUniform : SdfVariabilityVarying);
1642     }
1643 
1644     TRACE_SCOPE("UsdAbc_AlembicDataReader::_HasField:OtherMetadata");
1645     MetadataMap::const_iterator j = property->metadata.find(fieldName);
1646     if (j != property->metadata.end()) {
1647         return value.Set(j->second);
1648     }
1649 
1650     return false;
1651 }
1652 
1653 bool
_HasValue(const Property * property,const ISampleSelector & selector,const UsdAbc_AlembicDataAny & value) const1654 _ReaderContext::_HasValue(
1655     const Property* property,
1656     const ISampleSelector& selector,
1657     const UsdAbc_AlembicDataAny& value) const
1658 {
1659     TRACE_FUNCTION();
1660 
1661     // Check if no conversion available.
1662     if (!property->converter) {
1663         return false;
1664     }
1665 
1666     // See if only checking for existence.
1667     if (value.IsEmpty()) {
1668         return true;
1669     }
1670 
1671     TRACE_SCOPE("UsdAbc_AlembicDataReader::_HasValue:Conversion");
1672     _Lock lock(_mutex);
1673     return property->converter(value, selector);
1674 }
1675 
1676 //
1677 // Utilities
1678 //
1679 
1680 /// Return the number of interesting samples in an object
1681 template <typename T> size_t
_GetNumSamples(const T & object)1682 _GetNumSamples(const T& object) {
1683     size_t nSamples = object.getNumSamples();
1684     if (!object.isConstant())
1685         return nSamples;
1686 
1687     return std::min(nSamples, size_t(1));
1688 }
1689 
1690 /// Fill sample times from an object with getTimeSampling() and
1691 /// getNumSamples() methods.
1692 template <class T>
1693 static
1694 _AlembicTimeSamples
_GetSampleTimes(const T & object)1695 _GetSampleTimes(const T& object)
1696 {
1697     _AlembicTimeSamples result;
1698     if (object.valid()) {
1699         TimeSamplingPtr timeSampling = object.getTimeSampling();
1700         for (size_t i = 0, n = _GetNumSamples(object); i < n; ++i) {
1701             result.push_back(timeSampling->getSampleTime(i));
1702         }
1703     }
1704     return result;
1705 }
1706 
1707 template <class T>
1708 static
1709 _AlembicTimeSamples
_GetSampleTimes(const ISchemaObject<T> & object)1710 _GetSampleTimes(const ISchemaObject<T>& object)
1711 {
1712     return _GetSampleTimes(object.getSchema());
1713 }
1714 
1715 static
1716 TfToken
_GetRole(const std::string & role)1717 _GetRole(const std::string& role)
1718 {
1719     if (role.empty()) {
1720         return TfToken();
1721     }
1722     if (role == "point") {
1723         return SdfValueRoleNames->Point;
1724     }
1725     if (role == "normal") {
1726         return SdfValueRoleNames->Normal;
1727     }
1728     if (role == "vector") {
1729         return SdfValueRoleNames->Vector;
1730     }
1731     if (role == "rgb") {
1732         return SdfValueRoleNames->Color;
1733     }
1734     if (role == "point") {
1735         return SdfValueRoleNames->Point;
1736     }
1737     if (role == "rgba") {
1738         // No Usd types for RGBA colors.
1739         return TfToken();
1740     }
1741     if (role == "matrix") {
1742         // No special treatment.
1743         return TfToken();
1744     }
1745     if (role == "quat") {
1746         // Special case.
1747         return TfToken("quat");
1748     }
1749     // Unknown.
1750     return TfToken();
1751 }
1752 
1753 static
1754 SdfValueTypeName
_GetInterpretation(const SdfValueTypeName & typeName,const TfToken & role)1755 _GetInterpretation(const SdfValueTypeName& typeName, const TfToken& role)
1756 {
1757     if (role == "quat") {
1758         if (typeName == SdfValueTypeNames->Float4) {
1759             return SdfValueTypeNames->Quatf;
1760         }
1761         if (typeName == SdfValueTypeNames->Double4) {
1762             return SdfValueTypeNames->Quatd;
1763         }
1764     }
1765 
1766     // Get the type for the role, if any, otherwise use the input type.
1767     // Using the input type as a fallback will, among other things,
1768     // convert a float[2] with a "vector" interpretation to Float2;
1769     // note that there is no Vector2f in Usd so the FindType() would
1770     // yield an empty type in this case.
1771     SdfValueTypeName result =
1772         SdfSchema::GetInstance().FindType(typeName.GetType(), role);
1773     return result ? result : typeName;
1774 }
1775 
1776 static
1777 TfToken
_GetInterpolation(GeometryScope geometryScope)1778 _GetInterpolation(GeometryScope geometryScope)
1779 {
1780     static const TfToken constant("constant");
1781     static const TfToken uniform("uniform");
1782     static const TfToken varying("varying");
1783     static const TfToken vertex("vertex");
1784     static const TfToken faceVarying("faceVarying");
1785     switch (geometryScope) {
1786     case kConstantScope: return constant;
1787     case kUniformScope: return uniform;
1788     case kVaryingScope: return varying;
1789     case kVertexScope: return vertex;
1790     case kFacevaryingScope: return faceVarying;
1791     default: return TfToken();
1792     }
1793 }
1794 
1795 //
1796 // _PrimReaderContext
1797 //
1798 
1799 struct _IsValidTag { };
1800 struct _MetaDataTag { };
1801 struct _SampleTimesTag { };
1802 
1803 /// \class _PrimReaderContext
1804 /// \brief The Usd to Alembic prim reader context.
1805 class _PrimReaderContext {
1806 public:
1807     typedef _ReaderContext::Converter Converter;
1808     typedef _ReaderContext::Prim Prim;
1809     typedef _ReaderContext::Property Property;
1810     typedef _IsValidTag IsValidTag;
1811     typedef _MetaDataTag MetaDataTag;
1812     typedef _SampleTimesTag SampleTimesTag;
1813     typedef std::function<bool (IsValidTag)> IsValid;
1814     typedef std::function<const MetaData& (MetaDataTag)> GetMetaData;
1815     typedef std::function<_AlembicTimeSamples(SampleTimesTag)> GetSampleTimes;
1816 
1817     _PrimReaderContext(_ReaderContext&,
1818                        const IObject& prim,
1819                        const SdfPath& path);
1820 
1821     /// Returns the prim object.
1822     IObject GetObject() const;
1823 
1824     /// Returns the Usd path to this prim.
1825     const SdfPath& GetPath() const;
1826 
1827     /// Returns \p name converted to a valid Usd name not currently used
1828     /// by any property on this prim.
1829     std::string GetUsdName(const std::string& name) const;
1830 
1831     /// Returns the prim cache.
1832     Prim& GetPrim();
1833 
1834     /// Adds a property named \p name of type \p typeName with the converter
1835     /// \p converter.  \p converter must be a functor object that conforms
1836     /// to the \c IsValid, \c Converter, \c GetSampleTimes and \c GetMetaData
1837     /// types.  If \p convert is invalid then this does nothing.
1838     template <class T>
1839     void AddProperty(const TfToken& name, const SdfValueTypeName& typeName,
1840                      const T& converter);
1841 
1842     /// Adds a uniform property named \p name of type \p typeName with the
1843     /// converter \p converter.  \p converter must be a functor object that
1844     /// conforms to the \c IsValid, \c Converter, \c GetSampleTimes and
1845     /// \c GetMetaData types.  If \p convert is invalid then this does nothing.
1846     template <class T>
1847     void AddUniformProperty(const TfToken& name,
1848                             const SdfValueTypeName& typeName,
1849                             const T& converter);
1850 
1851     /// Add an out-of-schema property, which uses the default conversion
1852     /// for whatever Alembic type the property is.  If \p property is a
1853     /// compound property then all of its descendants are added as
1854     /// out-of-schema properties, building a namespaced name from the
1855     /// property hierarchy with \p name as the left-most component.
1856     void AddOutOfSchemaProperty(const std::string& name,
1857                                 const AlembicProperty& property);
1858 
1859     /// Set the schema.  This makes additional properties available via
1860     /// the \c ExtractSchema() method.
1861     void SetSchema(const std::string& schemaName);
1862 
1863     /// Get a sample from the schema.  This doesn't extract any properties.
1864     /// If the given type doesn't match the schema then this returns false
1865     /// otherwise it returns true.
1866     template <typename T>
GetSample(typename T::schema_type::Sample & sample,const ISampleSelector & iss) const1867     bool GetSample(typename T::schema_type::Sample& sample,
1868                    const ISampleSelector& iss) const
1869     {
1870         typename T::schema_type schema(_prim.getProperties());
1871         if (schema.valid()) {
1872             schema.get(sample, iss);
1873             return true;
1874         }
1875         return false;
1876     }
1877 
1878     /// Returns the alembic property named \p name.  If the property doesn't
1879     /// exist or has already been extracted then this returns an empty
1880     /// property object.  The property is extracted from the context so
1881     /// it cannot be extracted again.
1882     AlembicProperty Extract(const std::string& name);
1883 
1884     /// Returns the alembic property named \p name in the schema. If
1885     /// the property doesn't exist or has already been extracted then
1886     /// this returns an empty property object.  The property is extracted
1887     /// from the context so it cannot be extracted again.
1888     AlembicProperty ExtractSchema(const std::string& name);
1889 
1890     /// Returns the names of properties that have not been extracted yet
1891     /// in Alembic property order.
1892     std::vector<std::string> GetUnextractedNames() const;
1893 
1894     /// Returns a _PrimReaderContext corresponding to the parent of this context.
1895     _PrimReaderContext GetParentContext() const;
1896 
1897 private:
1898     Property& _AddProperty(const TfToken& name);
1899     Property& _AddProperty(const TfToken& name,
1900                            const SdfValueTypeName& typeName,
1901                            const GetMetaData& metadata,
1902                            const GetSampleTimes& sampleTimes,
1903                            bool isOutOfSchemaProperty);
1904     Property& _AddProperty(const TfToken& name,
1905                            const SdfValueTypeName& typeName,
1906                            const MetaData& metadata,
1907                            const _AlembicTimeSamples& sampleTimes,
1908                            bool isOutOfSchemaProperty);
1909     void _GetPropertyMetadata(const MetaData& alembicMetadata,
1910                               Property* property,
1911                               bool isOutOfSchemaProperty) const;
1912 
1913 private:
1914     _ReaderContext& _context;
1915     IObject _prim;
1916     ICompoundProperty _schema;
1917     SdfPath _path;
1918     std::vector<std::string> _unextracted;
1919     std::vector<std::string> _unextractedSchema;
1920     std::set<std::string> _usedNames;
1921 };
1922 
_PrimReaderContext(_ReaderContext & context,const IObject & prim,const SdfPath & path)1923 _PrimReaderContext::_PrimReaderContext(
1924     _ReaderContext& context,
1925     const IObject& prim,
1926     const SdfPath& path) :
1927     _context(context),
1928     _prim(prim),
1929     _path(path)
1930 {
1931     // Fill _unextracted with all of the property names.
1932     ICompoundProperty properties = _prim.getProperties();
1933     _unextracted.resize(properties.getNumProperties());
1934     for (size_t i = 0, n = _unextracted.size(); i != n; ++i) {
1935         _unextracted[i] = properties.getPropertyHeader(i).getName();
1936     }
1937 
1938     // Pre-populate _usedNames with the names as-is.
1939     _usedNames.insert(_unextracted.begin(), _unextracted.end());
1940 }
1941 
1942 IObject
GetObject() const1943 _PrimReaderContext::GetObject() const
1944 {
1945     return _prim;
1946 }
1947 
1948 const SdfPath&
GetPath() const1949 _PrimReaderContext::GetPath() const
1950 {
1951     return _path;
1952 }
1953 
1954 std::string
GetUsdName(const std::string & name) const1955 _PrimReaderContext::GetUsdName(const std::string& name) const
1956 {
1957     return _CleanName(name, " .", _usedNames,
1958                       _AlembicFixNamespacedName(),
1959                       &SdfPath::IsValidNamespacedIdentifier);
1960 }
1961 
1962 _PrimReaderContext::Prim&
GetPrim()1963 _PrimReaderContext::GetPrim()
1964 {
1965     return _context.AddPrim(GetPath());
1966 }
1967 
1968 void
SetSchema(const std::string & schemaName)1969 _PrimReaderContext::SetSchema(const std::string& schemaName)
1970 {
1971     _unextractedSchema.clear();
1972     _schema = ICompoundProperty(_prim.getProperties(), schemaName,
1973                                 ErrorHandler::kQuietNoopPolicy);
1974     if (_schema.valid()) {
1975         // Fill _unextractedSchema with all of the property names.
1976         _unextractedSchema.resize(_schema.getNumProperties());
1977         for (size_t i = 0, n = _unextractedSchema.size(); i != n; ++i) {
1978             _unextractedSchema[i] = _schema.getPropertyHeader(i).getName();
1979         }
1980     }
1981 
1982     // Pre-populate _usedNames with the names as-is.
1983     _usedNames.insert(_unextractedSchema.begin(), _unextractedSchema.end());
1984 }
1985 
1986 AlembicProperty
Extract(const std::string & name)1987 _PrimReaderContext::Extract(const std::string& name)
1988 {
1989     std::vector<std::string>::iterator i =
1990         std::find(_unextracted.begin(), _unextracted.end(), name);
1991     if (i != _unextracted.end()) {
1992         _unextracted.erase(i);
1993         return AlembicProperty(GetPath(), name, GetObject());
1994     }
1995     return AlembicProperty(GetPath(), name);
1996 }
1997 
1998 AlembicProperty
ExtractSchema(const std::string & name)1999 _PrimReaderContext::ExtractSchema(const std::string& name)
2000 {
2001     std::vector<std::string>::iterator i =
2002         std::find(_unextractedSchema.begin(), _unextractedSchema.end(), name);
2003     if (i != _unextractedSchema.end()) {
2004         _unextractedSchema.erase(i);
2005         return AlembicProperty(GetPath(), name, _schema);
2006     }
2007     return AlembicProperty(GetPath(), name);
2008 }
2009 
2010 std::vector<std::string>
GetUnextractedNames() const2011 _PrimReaderContext::GetUnextractedNames() const
2012 {
2013     return _unextracted;
2014 }
2015 
2016 template <class T>
2017 void
AddProperty(const TfToken & name,const SdfValueTypeName & typeName,const T & converter)2018 _PrimReaderContext::AddProperty(
2019     const TfToken& name,
2020     const SdfValueTypeName& typeName,
2021     const T& converter)
2022 {
2023     if (converter(IsValidTag())) {
2024         _AddProperty(name, typeName, converter, converter, false).converter=converter;
2025     }
2026 }
2027 
2028 template <class T>
2029 void
AddUniformProperty(const TfToken & name,const SdfValueTypeName & typeName,const T & converter)2030 _PrimReaderContext::AddUniformProperty(
2031     const TfToken& name,
2032     const SdfValueTypeName& typeName,
2033     const T& converter)
2034 {
2035     if (converter(IsValidTag())) {
2036         Property &prop = _AddProperty(name, typeName, converter, converter, false);
2037         prop.converter=converter;
2038         prop.uniform = true;
2039         prop.timeSampled = false;
2040     }
2041 }
2042 
2043 void
AddOutOfSchemaProperty(const std::string & name,const AlembicProperty & property)2044 _PrimReaderContext::AddOutOfSchemaProperty(
2045     const std::string& name,
2046     const AlembicProperty& property)
2047 {
2048     // Get property info.
2049     const PropertyHeader* header = property.GetHeader();
2050     if (!header) {
2051         // No such property.
2052         return;
2053     }
2054 
2055     // Handle compound properties.
2056     if (header->isCompound()) {
2057         ICompoundProperty compound = property.Cast<ICompoundProperty>();
2058 
2059         // Fill usedNames.
2060         std::set<std::string> usedNames;
2061         for (size_t i = 0, n = compound.getNumProperties(); i != n; ++i) {
2062             usedNames.insert(compound.getPropertyHeader(i).getName());
2063         }
2064 
2065         // Recurse.
2066         for (size_t i = 0, n = compound.getNumProperties(); i != n; ++i) {
2067             const std::string rawName =
2068                 compound.getPropertyHeader(i).getName();
2069             const std::string cleanName =
2070                 _CleanName(rawName, " .", usedNames,
2071                            _AlembicFixName(),
2072                            &SdfPath::IsValidIdentifier);
2073             const std::string namespacedName =
2074                 SdfPath::JoinIdentifier(name, cleanName);
2075             const SdfPath childPath =
2076                 GetPath().AppendProperty(TfToken(namespacedName));
2077             AddOutOfSchemaProperty(
2078                 namespacedName,
2079                 AlembicProperty(childPath, rawName, compound));
2080         }
2081 
2082         return;
2083     }
2084 
2085     // Get the sample times.
2086     _AlembicTimeSamples sampleTimes;
2087     if (header->isScalar()) {
2088         sampleTimes = _GetSampleTimes(property.Cast<IScalarProperty>());
2089     }
2090     else {
2091         sampleTimes = _GetSampleTimes(property.Cast<IArrayProperty>());
2092     }
2093 
2094     // Get the converter and add the property.
2095     const bool isOutOfSchema = true;
2096     const UsdAbc_AlembicType alembicType(*header);
2097     const SdfValueTypeName usdTypeName =
2098         _context.GetSchema().GetConversions().FindConverter(alembicType);
2099     if (usdTypeName) {
2100         _PrimReaderContext::Property &prop =
2101             (TfGetEnvSetting(USD_ABC_WRITE_UV_AS_ST_TEXCOORD2FARRAY) &&
2102              name == UsdAbcPropertyNames->uvIndices)?
2103                 (_AddProperty(UsdAbcPropertyNames->stIndices,
2104                              usdTypeName, header->getMetaData(),
2105                              sampleTimes, isOutOfSchema)) :
2106                 (_AddProperty(TfToken(name),
2107                              usdTypeName, header->getMetaData(),
2108                              sampleTimes, isOutOfSchema));
2109         prop.converter = std::bind(
2110             _context.GetSchema().GetConversions().GetToUsdConverter(
2111                 alembicType, prop.typeName),
2112             property.GetParent(), property.GetName(),
2113             std::placeholders::_2, std::placeholders::_1);
2114     }
2115     else {
2116         TF_WARN("No conversion for \"%s\" of type \"%s\" at <%s>",
2117                 name.c_str(),
2118                 alembicType.Stringify().c_str(),
2119                 GetPath().GetText());
2120     }
2121 }
2122 
2123 _PrimReaderContext::Property&
_AddProperty(const TfToken & name)2124 _PrimReaderContext::_AddProperty(const TfToken& name)
2125 {
2126     // Create the property cache and add the property to the prim's
2127     // properties.
2128     const SdfPath path = GetPath().AppendProperty(name);
2129     if (_context.FindProperty(path)) {
2130         // Existing property.  We allow duplicate properties in order to
2131         // support multiple Alembic sources (particularly for color).
2132     }
2133     else {
2134         // New property.
2135         GetPrim().properties.push_back(name);
2136         _usedNames.insert(name);
2137     }
2138     return _context.FindOrCreateProperty(path);
2139 }
2140 
2141 _PrimReaderContext::Property&
_AddProperty(const TfToken & name,const SdfValueTypeName & typeName,const GetMetaData & metadata,const GetSampleTimes & sampleTimes,bool isOutOfSchemaProperty)2142 _PrimReaderContext::_AddProperty(
2143     const TfToken& name,
2144     const SdfValueTypeName& typeName,
2145     const GetMetaData& metadata,
2146     const GetSampleTimes& sampleTimes,
2147     bool isOutOfSchemaProperty)
2148 {
2149     return _AddProperty(name, typeName,
2150                         metadata(MetaDataTag()),
2151                         sampleTimes(SampleTimesTag()),
2152                         isOutOfSchemaProperty);
2153 }
2154 
2155 _PrimReaderContext::Property&
_AddProperty(const TfToken & name,const SdfValueTypeName & typeName,const MetaData & metadata,const _AlembicTimeSamples & sampleTimes,bool isOutOfSchemaProperty)2156 _PrimReaderContext::_AddProperty(
2157     const TfToken& name,
2158     const SdfValueTypeName& typeName,
2159     const MetaData& metadata,
2160     const _AlembicTimeSamples& sampleTimes,
2161     bool isOutOfSchemaProperty)
2162 {
2163     // Make the property.
2164     Property& property = _AddProperty(name);
2165 
2166     // Save the Usd type.
2167     property.typeName = typeName;
2168 
2169     // Save the time sampling.
2170     property.sampleTimes = _context.ConvertSampleTimes(sampleTimes);
2171     property.timeSampled = (property.sampleTimes.GetSize() > 0);
2172 
2173     // Save metadata.  This may change the value of 'timeSampled'
2174     _GetPropertyMetadata(metadata, &property, isOutOfSchemaProperty);
2175 
2176     if (property.timeSampled) {
2177         _context.AddSampleTimes(property.sampleTimes);
2178     }
2179 
2180 #ifdef USDABC_ALEMBIC_DEBUG
2181     fprintf(stdout, "%*s%s%s %s\n",
2182             2 * ((int)GetPath().GetPathElementCount() + 1), "",
2183             property.metadata[SdfFieldKeys->Custom].UncheckedGet<bool>()
2184                 ? "custom " : "",
2185             property.typeName.GetAsToken().GetText(), name.GetText());
2186 #endif
2187 
2188     return property;
2189 }
2190 
2191 void
_GetPropertyMetadata(const MetaData & alembicMetadata,Property * property,bool isOutOfSchemaProperty) const2192 _PrimReaderContext::_GetPropertyMetadata(
2193     const MetaData& alembicMetadata,
2194     Property* property,
2195     bool isOutOfSchemaProperty) const
2196 {
2197     std::string value;
2198     MetadataMap& usdMetadata = property->metadata;
2199 
2200     // Custom.  This is required metadata.
2201     usdMetadata[SdfFieldKeys->Custom] = VtValue(isOutOfSchemaProperty);
2202     _GetBoolMetadata(alembicMetadata, usdMetadata, SdfFieldKeys->Custom);
2203 
2204     // Variability.  This is required metadata.
2205     if (alembicMetadata.get(_AmdName(SdfFieldKeys->Variability)) == "uniform") {
2206         usdMetadata[SdfFieldKeys->Variability] = SdfVariabilityUniform;
2207     }
2208     else {
2209         usdMetadata[SdfFieldKeys->Variability] = SdfVariabilityVarying;
2210     }
2211 
2212     // Type name.
2213     if (!property->typeName) {
2214         property->typeName =
2215             SdfSchema::GetInstance().FindType(
2216                 alembicMetadata.get(_AmdName(SdfFieldKeys->TypeName)));
2217     }
2218 
2219     // If there's only one timeSample and we've indicated it should be
2220     // converted into Default, disable timeSampling.
2221     if (property->sampleTimes.GetSize() == 1 &&
2222         alembicMetadata.get(_AmdName(UsdAbcCustomMetadata->singleSampleAsDefault)) == "true") {
2223         property->timeSampled = false;
2224     }
2225 
2226     // Adjust the type name by the interpretation.
2227     property->typeName =
2228         _GetInterpretation(property->typeName,
2229                            _GetRole(alembicMetadata.get("interpretation")));
2230 
2231     // Set the interpolation if there is one.
2232     if (!alembicMetadata.get("geoScope").empty()) {
2233         const TfToken interpolation =
2234             _GetInterpolation(GetGeometryScope(alembicMetadata));
2235         if (!interpolation.IsEmpty()) {
2236             usdMetadata[UsdGeomTokens->interpolation] = VtValue(interpolation);
2237         }
2238     }
2239 
2240     // Other Sdf metadata.
2241     _GetStringMetadata(alembicMetadata, usdMetadata, SdfFieldKeys->DisplayGroup);
2242     _GetStringMetadata(alembicMetadata, usdMetadata, SdfFieldKeys->Documentation);
2243     _GetBoolMetadata(alembicMetadata, usdMetadata, SdfFieldKeys->Hidden);
2244 
2245     // Custom metadata.
2246     _GetStringMetadata(alembicMetadata, usdMetadata,
2247                        UsdAbcCustomMetadata->riName);
2248     _GetStringMetadata(alembicMetadata, usdMetadata,
2249                        UsdAbcCustomMetadata->riType);
2250     _GetBoolMetadata(alembicMetadata, usdMetadata,
2251                      UsdAbcCustomMetadata->gprimDataRender);
2252 }
2253 
2254 _PrimReaderContext
GetParentContext() const2255 _PrimReaderContext::GetParentContext() const
2256 {
2257     _PrimReaderContext parentContext(
2258         _context,
2259         _prim.getParent(),
2260         _path.GetParentPath());
2261     return parentContext;
2262 }
2263 
2264 //
2265 // Copy functors
2266 //
2267 
2268 /// Convert a scalar property value to a VtValue.
2269 template <class AlembicTraits, class UsdType, class AlembicType>
2270 static
2271 VtValue
_CopyGenericValue(const AlembicType & src)2272 _CopyGenericValue(const AlembicType& src)
2273 {
2274     typedef typename PODTraitsFromEnum<
2275         AlembicTraits::pod_enum>::value_type SrcType;
2276     static const int SrcExtent = AlembicTraits::extent;
2277 
2278     // All Alembic data is packed POD elements so we can simply use the
2279     // address of src as the address of the first POD element.
2280     const SrcType* srcData = reinterpret_cast<const SrcType*>(&src);
2281 
2282     // Convert.
2283     return VtValue(_ConvertPODToUsd<UsdType, SrcType, SrcExtent>()(*srcData));
2284 }
2285 
2286 /// Convert an array property value to a VtValue.
2287 template <class AlembicTraits, class UsdType>
2288 static
2289 VtValue
_CopyGenericValue(const shared_ptr<TypedArraySample<AlembicTraits>> & src)2290 _CopyGenericValue(const shared_ptr<TypedArraySample<AlembicTraits> >& src)
2291 {
2292     typedef typename PODTraitsFromEnum<
2293         AlembicTraits::pod_enum>::value_type SrcType;
2294     static const int SrcExtent = AlembicTraits::extent;
2295 
2296     // All Alembic data is packed POD elements so we can simply use the
2297     // address of src as the address of the first POD element.
2298     const SrcType* srcData = reinterpret_cast<const SrcType*>(src->get());
2299 
2300     // Convert.
2301     VtArray<UsdType> result(src->size());
2302     _ConvertPODToUsdArray<UsdType, SrcType, SrcExtent>()(result.data(),
2303                                                          srcData, src->size());
2304     return VtValue(result);
2305 }
2306 
2307 /// Return a constant (default) value.
2308 struct _CopySynthetic {
2309     VtValue value;
2310     MetaData metadata;
2311     template <class T>
_CopySynthetic__anon67d290af0111::_CopySynthetic2312     _CopySynthetic(const T& value_) : value(value_) { }
2313 
operator ()__anon67d290af0111::_CopySynthetic2314     bool operator()(_IsValidTag) const
2315     {
2316         return true;
2317     }
2318 
operator ()__anon67d290af0111::_CopySynthetic2319     const MetaData& operator()(_MetaDataTag) const
2320     {
2321         return metadata;
2322     }
2323 
operator ()__anon67d290af0111::_CopySynthetic2324     _AlembicTimeSamples operator()(_SampleTimesTag) const
2325     {
2326         return _AlembicTimeSamples(1, 0.0);
2327     }
2328 
operator ()__anon67d290af0111::_CopySynthetic2329     bool operator()(const UsdAbc_AlembicDataAny& dst,
2330                     const ISampleSelector& iss) const
2331     {
2332         return dst.Set(value);
2333     }
2334 };
2335 
2336 /// Copy a value from a property of a given type to \c UsdValueType.
2337 template <class T, class UsdValueType = void, bool expand = true>
2338 struct _CopyGeneric {
2339     typedef T PropertyType;
2340     // This is defined for ITyped*Property.
2341     typedef typename PropertyType::traits_type AlembicTraits;
2342 
2343     PropertyType object;
_CopyGeneric__anon67d290af0111::_CopyGeneric2344     _CopyGeneric(const AlembicProperty& object_,
2345                  SchemaInterpMatching matching = kStrictMatching) :
2346         object(object_.Cast<PropertyType>(matching)) { }
2347 
operator ()__anon67d290af0111::_CopyGeneric2348     bool operator()(_IsValidTag) const
2349     {
2350         return object.valid();
2351     }
2352 
operator ()__anon67d290af0111::_CopyGeneric2353     const MetaData& operator()(_MetaDataTag) const
2354     {
2355         return object.getMetaData();
2356     }
2357 
operator ()__anon67d290af0111::_CopyGeneric2358     _AlembicTimeSamples operator()(_SampleTimesTag) const
2359     {
2360         return _GetSampleTimes(object);
2361     }
2362 
operator ()__anon67d290af0111::_CopyGeneric2363     bool operator()(const UsdAbc_AlembicDataAny& dst,
2364                     const ISampleSelector& iss) const
2365     {
2366         return dst.Set(_CopyGenericValue<AlembicTraits,
2367                                          UsdValueType>(object.getValue(iss)));
2368     }
2369 };
2370 
2371 /// Copy a ITypedGeomParam.  These are either an ITypedArrayProperty or a
2372 /// compound property with an ITypedArrayProperty and indices. If the template
2373 /// parameter `expand` is true (which is the default), then we will call
2374 /// getExpanced() to get the un-indexed values. Otherwise we will get the
2375 /// indexed values, but _CopyIndices will need to be used to extract the
2376 /// indices.
2377 template <class T, class UsdValueType, bool expand>
2378 struct _CopyGeneric<ITypedGeomParam<T>, UsdValueType, expand> {
2379     typedef ITypedGeomParam<T> GeomParamType;
2380     typedef typename GeomParamType::prop_type PropertyType;
2381     typedef typename PropertyType::traits_type AlembicTraits;
2382 
2383     GeomParamType object;
_CopyGeneric__anon67d290af0111::_CopyGeneric2384     _CopyGeneric(const AlembicProperty& object_) :
2385         object(object_.Cast<GeomParamType>()) { }
2386 
operator ()__anon67d290af0111::_CopyGeneric2387     bool operator()(_IsValidTag) const
2388     {
2389         return object.valid();
2390     }
2391 
operator ()__anon67d290af0111::_CopyGeneric2392     const MetaData& operator()(_MetaDataTag) const
2393     {
2394         return object.getMetaData();
2395     }
2396 
operator ()__anon67d290af0111::_CopyGeneric2397     _AlembicTimeSamples operator()(_SampleTimesTag) const
2398     {
2399         return _GetSampleTimes(object);
2400     }
2401 
operator ()__anon67d290af0111::_CopyGeneric2402     bool operator()(const UsdAbc_AlembicDataAny& dst,
2403                     const ISampleSelector& iss) const
2404     {
2405         typename GeomParamType::sample_type sample;
2406         if (expand == false && object.isIndexed()) {
2407             object.getIndexed(sample, iss);
2408         } else {
2409             object.getExpanded(sample, iss);
2410         }
2411         return dst.Set(_CopyGenericValue<AlembicTraits,
2412                                          UsdValueType>(sample.getVals()));
2413     }
2414 };
2415 
2416 /// Copy a ITypedGeomParam's index list as an int array.
2417 /// If the Alembic property is not indexed, it will do nothing.
2418 template <class GeomParamType>
2419 struct _CopyIndices {
2420     typedef typename GeomParamType::prop_type PropertyType;
2421     typedef typename PropertyType::traits_type AlembicTraits;
2422 
2423     GeomParamType object;
_CopyIndices__anon67d290af0111::_CopyIndices2424     _CopyIndices(const AlembicProperty& object_) :
2425         object(object_.Cast<GeomParamType>())
2426     {
2427     }
2428 
operator ()__anon67d290af0111::_CopyIndices2429     bool operator()(_IsValidTag) const
2430     {
2431         return object.valid();
2432     }
2433 
operator ()__anon67d290af0111::_CopyIndices2434     const MetaData& operator()(_MetaDataTag) const
2435     {
2436         return object.getMetaData();
2437     }
2438 
operator ()__anon67d290af0111::_CopyIndices2439     _AlembicTimeSamples operator()(_SampleTimesTag) const
2440     {
2441         return _GetSampleTimes(object);
2442     }
2443 
operator ()__anon67d290af0111::_CopyIndices2444     bool operator()(const UsdAbc_AlembicDataAny& dst,
2445                     const ISampleSelector& iss) const
2446     {
2447         if (object.isIndexed()) {
2448             typename GeomParamType::sample_type sample;
2449             object.getIndexed(sample, iss);
2450             return dst.Set(_CopyGenericValue<Uint32TPTraits,
2451                                              int>(sample.getIndices()));
2452         }
2453         return false;
2454     }
2455 };
2456 
2457 /// Copy hermite points from interleaved IP3fArrayProperty.
2458 struct _CopyHermitePoints : _CopyGeneric<IP3fArrayProperty, GfVec3f> {
_CopyHermitePoints__anon67d290af0111::_CopyHermitePoints2459     _CopyHermitePoints(const AlembicProperty& object_)
2460         : _CopyGeneric<IP3fArrayProperty, GfVec3f>(object_, kStrictMatching) {}
2461 
2462     using _CopyGeneric<IP3fArrayProperty, GfVec3f>::operator();
2463 
operator ()__anon67d290af0111::_CopyHermitePoints2464     bool operator()(const UsdAbc_AlembicDataAny& dst,
2465                     const ISampleSelector& iss) const {
2466         typedef typename IP3fArrayProperty::sample_ptr_type SamplePtr;
2467         typedef typename IP3fArrayProperty::traits_type AlembicTraits;
2468         SamplePtr samplePtr;
2469         object.get(samplePtr, iss);
2470         VtValue value = _CopyGenericValue<AlembicTraits, GfVec3f>(samplePtr);
2471         VtVec3fArray interleaved = value.Get<VtVec3fArray>();
2472         auto separated =
2473             UsdGeomHermiteCurves::PointAndTangentArrays::Separate(interleaved);
2474         return dst.Set(VtValue(separated.GetPoints()));
2475     }
2476 };
2477 
2478 /// Copy hermite points from interleaved IP3fArrayProperty.
2479 struct _CopyHermiteTangents : _CopyGeneric<IP3fArrayProperty, GfVec3f> {
_CopyHermiteTangents__anon67d290af0111::_CopyHermiteTangents2480     _CopyHermiteTangents(const AlembicProperty& object_)
2481         : _CopyGeneric<IP3fArrayProperty, GfVec3f>(object_, kStrictMatching) {}
2482 
2483     using _CopyGeneric<IP3fArrayProperty, GfVec3f>::operator();
2484 
operator ()__anon67d290af0111::_CopyHermiteTangents2485     bool operator()(const UsdAbc_AlembicDataAny& dst,
2486                     const ISampleSelector& iss) const {
2487         typedef typename IP3fArrayProperty::sample_ptr_type SamplePtr;
2488         typedef typename IP3fArrayProperty::traits_type AlembicTraits;
2489         SamplePtr samplePtr;
2490         object.get(samplePtr, iss);
2491         VtValue value = _CopyGenericValue<AlembicTraits, GfVec3f>(samplePtr);
2492         VtVec3fArray interleaved = value.Get<VtVec3fArray>();
2493         auto separated =
2494             UsdGeomHermiteCurves::PointAndTangentArrays::Separate(interleaved);
2495         return dst.Set(VtValue(separated.GetTangents()));
2496     }
2497 };
2498 
2499 /// Copy a bounding box from an IBox3dProperty.
2500 struct _CopyBoundingBox : _CopyGeneric<IBox3dProperty> {
_CopyBoundingBox__anon67d290af0111::_CopyBoundingBox2501     _CopyBoundingBox(const AlembicProperty& object_) :
2502         _CopyGeneric<IBox3dProperty>(object_) { }
2503 
2504     using _CopyGeneric<IBox3dProperty>::operator();
2505 
operator ()__anon67d290af0111::_CopyBoundingBox2506     bool operator()(const UsdAbc_AlembicDataAny& dst,
2507                     const ISampleSelector& iss) const
2508     {
2509         const Box3d box = object.getValue(iss);
2510         const double* p = reinterpret_cast<const double*>(&box);
2511         VtArray<GfVec3f> result(2);
2512         result[0] = GfVec3f(p[0], p[1], p[2]);
2513         result[1] = GfVec3f(p[3], p[4], p[5]);
2514         return dst.Set(result);
2515     }
2516 };
2517 
2518 /// Copy a orientation from an IStringProperty.
2519 struct _CopyOrientation : _CopyGeneric<IStringProperty> {
_CopyOrientation__anon67d290af0111::_CopyOrientation2520     _CopyOrientation(const AlembicProperty& object_) :
2521         _CopyGeneric<IStringProperty>(object_) { }
2522 
2523     using _CopyGeneric<IStringProperty>::operator();
2524 
operator ()__anon67d290af0111::_CopyOrientation2525     bool operator()(const UsdAbc_AlembicDataAny& dst,
2526                     const ISampleSelector& iss) const
2527     {
2528         return dst.Set(TfToken(object.getValue(iss)));
2529     }
2530 };
2531 
2532 /// Copy a visibility from an ICharProperty.
2533 struct _CopyVisibility : _CopyGeneric<ICharProperty> {
_CopyVisibility__anon67d290af0111::_CopyVisibility2534     _CopyVisibility(const AlembicProperty& object_) :
2535         _CopyGeneric<ICharProperty>(object_) { }
2536 
2537     using _CopyGeneric<ICharProperty>::operator();
2538 
operator ()__anon67d290af0111::_CopyVisibility2539     bool operator()(const UsdAbc_AlembicDataAny& dst,
2540                     const ISampleSelector& iss) const
2541     {
2542         const ObjectVisibility vis =
2543             static_cast<ObjectVisibility>(object.getValue(iss));
2544         switch (vis) {
2545         case kVisibilityHidden:
2546             return dst.Set(UsdGeomTokens->invisible);
2547 
2548         default:
2549         case kVisibilityVisible:
2550         {
2551             const std::string authoredValue =
2552                 (vis == kVisibilityVisible ?
2553                  "kVisibilityVisible" : TfStringify(vis));
2554             // Usd doesn't support this.
2555             _PostUnsupportedValueWarning(
2556                 object, iss, WarningVisibility,
2557                 authoredValue.c_str(), "kVisibilityDeferred");
2558             // Fall through
2559         }
2560         case kVisibilityDeferred:
2561             return dst.Set(UsdGeomTokens->inherited);
2562         }
2563     }
2564 };
2565 
2566 /// Copy a color from Maya export.
2567 struct _CopyAdskColor : _CopyGeneric<IC4fProperty> {
_CopyAdskColor__anon67d290af0111::_CopyAdskColor2568     _CopyAdskColor(const AlembicProperty& object_) :
2569         _CopyGeneric<IC4fProperty>(object_) { }
2570 
2571     using _CopyGeneric<IC4fProperty>::operator();
2572 
operator ()__anon67d290af0111::_CopyAdskColor2573     bool operator()(const UsdAbc_AlembicDataAny& dst,
2574                     const ISampleSelector& iss) const
2575     {
2576         const C4f color = object.getValue(iss);
2577         VtArray<GfVec3f> result(1);
2578         result[0] = GfVec3f(color[0], color[1], color[2]);
2579         return dst.Set(result);
2580     }
2581 };
2582 
2583 /// Copy a Transform from an IXform.
2584 struct _CopyXform {
2585     IXform object;
_CopyXform__anon67d290af0111::_CopyXform2586     _CopyXform(const IXform& object_) : object(object_) { }
2587 
operator ()__anon67d290af0111::_CopyXform2588     bool operator()(_IsValidTag) const
2589     {
2590         return object.valid();
2591     }
2592 
operator ()__anon67d290af0111::_CopyXform2593     const MetaData& operator()(_MetaDataTag) const
2594     {
2595         // Extract any metadata provided by the Usd Alembic writer.
2596         if (!_metadata) {
2597             _metadata = MetaData();
2598             const MetaData& srcMetadata = object.getMetaData();
2599             for (MetaData::const_iterator i  = srcMetadata.begin();
2600                                           i != srcMetadata.end(); ++i) {
2601                 if (!i->second.empty() &&
2602                         i->first.compare(0, 14, "Usd.transform:") == 0) {
2603                     _metadata->set(i->first.substr(14), i->second);
2604                 }
2605             }
2606         }
2607         return *_metadata;
2608     }
2609 
operator ()__anon67d290af0111::_CopyXform2610     _AlembicTimeSamples operator()(_SampleTimesTag) const
2611     {
2612         return _GetSampleTimes(object);
2613     }
2614 
operator ()__anon67d290af0111::_CopyXform2615     bool operator()(const UsdAbc_AlembicDataAny& dst,
2616                     const ISampleSelector& iss) const
2617     {
2618         M44d matrix = object.getSchema().getValue(iss).getMatrix();
2619         return dst.Set(GfMatrix4d(matrix.x));
2620     }
2621 
2622 private:
2623     mutable boost::optional<MetaData> _metadata;
2624 };
2625 
2626 /// Base class to copy attributes of an almebic camera to a USD camera
2627 struct _CopyCameraBase {
2628     ICamera object;
2629 
_CopyCameraBase__anon67d290af0111::_CopyCameraBase2630     _CopyCameraBase(const ICamera& object_) : object(object_) { }
2631 
operator ()__anon67d290af0111::_CopyCameraBase2632     bool operator()(_IsValidTag) const
2633     {
2634         return object.valid();
2635     }
2636 
operator ()__anon67d290af0111::_CopyCameraBase2637     const MetaData& operator()(_MetaDataTag) const
2638     {
2639         return object.getMetaData();
2640     }
2641 
operator ()__anon67d290af0111::_CopyCameraBase2642     _AlembicTimeSamples operator()(_SampleTimesTag) const
2643     {
2644         return _GetSampleTimes(object);
2645     }
2646 };
2647 
2648 /// Class to copy focal length
2649 struct _CopyCameraFocalLength : _CopyCameraBase {
2650     using _CopyCameraBase::operator();
2651 
_CopyCameraFocalLength__anon67d290af0111::_CopyCameraFocalLength2652     _CopyCameraFocalLength(const ICamera& object_)
2653         : _CopyCameraBase(object_) {};
2654 
operator ()__anon67d290af0111::_CopyCameraFocalLength2655     bool operator()(const UsdAbc_AlembicDataAny& dst,
2656                     const ISampleSelector& iss) const
2657     {
2658         // Focal Length is just copied into USD.
2659         // Both use mm as unit.
2660         const CameraSample sample = object.getSchema().getValue(iss);
2661         const float focalLength = sample.getFocalLength();
2662         return dst.Set(focalLength);
2663     }
2664 };
2665 
2666 /// Class to copy horizontal aperture
2667 struct _CopyCameraHorizontalAperture : _CopyCameraBase {
2668     using _CopyCameraBase::operator();
2669 
_CopyCameraHorizontalAperture__anon67d290af0111::_CopyCameraHorizontalAperture2670     _CopyCameraHorizontalAperture(const ICamera& object_)
2671         : _CopyCameraBase(object_) {};
2672 
operator ()__anon67d290af0111::_CopyCameraHorizontalAperture2673     bool operator()(const UsdAbc_AlembicDataAny& dst,
2674                     const ISampleSelector& iss) const
2675     {
2676         const CameraSample sample = object.getSchema().getValue(iss);
2677 
2678         // USD uses mm, alembic uses cm
2679 
2680         const float horizontalAperture =
2681             sample.getHorizontalAperture() *
2682             sample.getLensSqueezeRatio() * 10.0;
2683 
2684         return dst.Set(horizontalAperture);
2685     }
2686 };
2687 
2688 /// Class to copy vertical aperture
2689 struct _CopyCameraVerticalAperture : _CopyCameraBase {
2690     using _CopyCameraBase::operator();
2691 
_CopyCameraVerticalAperture__anon67d290af0111::_CopyCameraVerticalAperture2692     _CopyCameraVerticalAperture(const ICamera& object_)
2693         : _CopyCameraBase(object_) {};
2694 
operator ()__anon67d290af0111::_CopyCameraVerticalAperture2695     bool operator()(const UsdAbc_AlembicDataAny& dst,
2696                     const ISampleSelector& iss) const
2697     {
2698         const CameraSample sample = object.getSchema().getValue(iss);
2699 
2700         // USD uses mm, alembic uses cm
2701 
2702         const float verticalAperture =
2703             sample.getVerticalAperture() *
2704             sample.getLensSqueezeRatio() * 10.0;
2705 
2706         return dst.Set(verticalAperture);
2707     }
2708 };
2709 
2710 /// Class to copy horizontal aperture offset
2711 struct _CopyCameraHorizontalApertureOffset : _CopyCameraBase {
2712     using _CopyCameraBase::operator();
2713 
_CopyCameraHorizontalApertureOffset__anon67d290af0111::_CopyCameraHorizontalApertureOffset2714     _CopyCameraHorizontalApertureOffset(const ICamera& object_)
2715         : _CopyCameraBase(object_) {};
2716 
operator ()__anon67d290af0111::_CopyCameraHorizontalApertureOffset2717     bool operator()(const UsdAbc_AlembicDataAny& dst,
2718                     const ISampleSelector& iss) const
2719     {
2720         const CameraSample sample = object.getSchema().getValue(iss);
2721 
2722         // USD uses mm, alembic uses cm
2723 
2724         const float horizontalApertureOffset =
2725             sample.getHorizontalFilmOffset() *
2726             sample.getLensSqueezeRatio() * 10.0;
2727 
2728         return dst.Set(horizontalApertureOffset);
2729     }
2730 };
2731 
2732 /// Class to copy vertical aperture offset
2733 struct _CopyCameraVerticalApertureOffset : _CopyCameraBase {
2734     using _CopyCameraBase::operator();
2735 
_CopyCameraVerticalApertureOffset__anon67d290af0111::_CopyCameraVerticalApertureOffset2736     _CopyCameraVerticalApertureOffset(const ICamera& object_)
2737         : _CopyCameraBase(object_) {};
2738 
operator ()__anon67d290af0111::_CopyCameraVerticalApertureOffset2739     bool operator()(const UsdAbc_AlembicDataAny& dst,
2740                     const ISampleSelector& iss) const
2741     {
2742         const CameraSample sample = object.getSchema().getValue(iss);
2743 
2744         // USD uses mm, alembic uses cm
2745 
2746         const float verticalApertureOffset =
2747             sample.getVerticalFilmOffset() *
2748             sample.getLensSqueezeRatio() * 10.0;
2749         return dst.Set(verticalApertureOffset);
2750     }
2751 };
2752 
2753 /// Class to copy clippingRange
2754 struct _CopyCameraClippingRange : _CopyCameraBase {
2755     using _CopyCameraBase::operator();
2756 
_CopyCameraClippingRange__anon67d290af0111::_CopyCameraClippingRange2757     _CopyCameraClippingRange(const ICamera& object_)
2758         : _CopyCameraBase(object_) {};
2759 
operator ()__anon67d290af0111::_CopyCameraClippingRange2760     bool operator()(const UsdAbc_AlembicDataAny& dst,
2761                     const ISampleSelector& iss) const
2762     {
2763         const CameraSample sample = object.getSchema().getValue(iss);
2764 
2765         return dst.Set(GfVec2f(sample.getNearClippingPlane(),
2766                                sample.getFarClippingPlane()));
2767     }
2768 };
2769 
2770 /// Copy a subdivision scheme from an IStringProperty.
2771 struct _CopySubdivisionScheme : _CopyGeneric<IStringProperty> {
_CopySubdivisionScheme__anon67d290af0111::_CopySubdivisionScheme2772     _CopySubdivisionScheme(const AlembicProperty& object_) :
2773         _CopyGeneric<IStringProperty>(object_) { }
2774 
2775     using _CopyGeneric<IStringProperty>::operator();
2776 
operator ()__anon67d290af0111::_CopySubdivisionScheme2777     bool operator()(const UsdAbc_AlembicDataAny& dst,
2778                     const ISampleSelector& iss) const
2779     {
2780         const std::string value = object.getValue(iss);
2781         if (value.empty() || value == "catmull-clark") {
2782             return dst.Set(UsdGeomTokens->catmullClark);
2783         }
2784         if (value == "loop") {
2785             return dst.Set(UsdGeomTokens->loop);
2786         }
2787         if (value == "bilinear") {
2788             return dst.Set(UsdGeomTokens->bilinear);
2789         }
2790 
2791         _PostUnsupportedValueWarning(
2792             object, iss, WarningSubdivisionScheme, value, "catmull-clark");
2793         return dst.Set(UsdGeomTokens->catmullClark);
2794     }
2795 };
2796 
2797 /// Copy an interpolate boundary from an IInt32Property.
2798 struct _CopyInterpolateBoundary : _CopyGeneric<IInt32Property> {
_CopyInterpolateBoundary__anon67d290af0111::_CopyInterpolateBoundary2799     _CopyInterpolateBoundary(const AlembicProperty& object_) :
2800         _CopyGeneric<IInt32Property>(object_) { }
2801 
2802     using _CopyGeneric<IInt32Property>::operator();
2803 
operator ()__anon67d290af0111::_CopyInterpolateBoundary2804     bool operator()(const UsdAbc_AlembicDataAny& dst,
2805                     const ISampleSelector& iss) const
2806     {
2807         switch (object.getValue(iss)) {
2808         case 1:
2809             return dst.Set(UsdGeomTokens->edgeAndCorner);
2810 
2811         case 2:
2812             return dst.Set(UsdGeomTokens->edgeOnly);
2813 
2814         default:
2815             _PostUnsupportedValueWarning(
2816                 object, iss, WarningInterpolateBoundary,
2817                 TfStringify(object.getValue(iss)).c_str(), "0");
2818             // Fall-through
2819         case 0:
2820             return dst.Set(UsdGeomTokens->none);
2821         }
2822     }
2823 };
2824 
2825 /// Copy a face varying interpolate boundary from an IInt32Property.
2826 struct _CopyFaceVaryingInterpolateBoundary : _CopyGeneric<IInt32Property> {
_CopyFaceVaryingInterpolateBoundary__anon67d290af0111::_CopyFaceVaryingInterpolateBoundary2827     _CopyFaceVaryingInterpolateBoundary(const AlembicProperty& object_) :
2828         _CopyGeneric<IInt32Property>(object_) { }
2829 
2830     using _CopyGeneric<IInt32Property>::operator();
2831 
operator ()__anon67d290af0111::_CopyFaceVaryingInterpolateBoundary2832     bool operator()(const UsdAbc_AlembicDataAny& dst,
2833                     const ISampleSelector& iss) const
2834     {
2835         switch (object.getValue(iss)) {
2836         case 1:
2837             return dst.Set(UsdGeomTokens->cornersPlus1);
2838 
2839         case 2:
2840             return dst.Set(UsdGeomTokens->none);
2841 
2842         case 3:
2843             return dst.Set(UsdGeomTokens->boundaries);
2844 
2845         default:
2846             _PostUnsupportedValueWarning(
2847                 object, iss, WarningFaceVaryingInterpolateBoundary,
2848                 TfStringify(object.getValue(iss)).c_str(), "0");
2849             // Fall-through
2850         case 0:
2851             return dst.Set(UsdGeomTokens->all);
2852         }
2853     }
2854 };
2855 
2856 
2857 /// Base class to copy attributes of an alembic faceset to a USD GeomSubset
2858 struct _CopyFaceSetBase {
2859     IFaceSet object;
2860 
_CopyFaceSetBase__anon67d290af0111::_CopyFaceSetBase2861     _CopyFaceSetBase(const IFaceSet& object_) : object(object_) { }
2862 
operator ()__anon67d290af0111::_CopyFaceSetBase2863     bool operator()(_IsValidTag) const
2864     {
2865         return object.valid();
2866     }
2867 
operator ()__anon67d290af0111::_CopyFaceSetBase2868     const MetaData& operator()(_MetaDataTag) const
2869     {
2870         return object.getMetaData();
2871     }
2872 
operator ()__anon67d290af0111::_CopyFaceSetBase2873     _AlembicTimeSamples operator()(_SampleTimesTag) const
2874     {
2875         return _GetSampleTimes(object);
2876     }
2877 };
2878 
2879 /// Class to copy faceset isPartition into the family type
2880 struct _CopyFaceSetFamilyType : _CopyFaceSetBase {
2881     using _CopyFaceSetBase::operator();
2882 
_CopyFaceSetFamilyType__anon67d290af0111::_CopyFaceSetFamilyType2883     _CopyFaceSetFamilyType(const IFaceSet& object_)
2884         : _CopyFaceSetBase(object_) {};
2885 
operator ()__anon67d290af0111::_CopyFaceSetFamilyType2886     bool operator()(const UsdAbc_AlembicDataAny& dst,
2887                     const ISampleSelector& iss) const
2888     {
2889         // Because the absence of the ".facesExclusive" will trigger an
2890         // exception in IFaceSetSchema, we need to manually deal with the
2891         // default state (and discard the exception thrown erroneously by
2892         // getFaceExclusivity()).
2893         //
2894         // This is a bug in Alembic that has been fixed in Alembic 1.7.2, but
2895         // the mininum required version for USD is 1.5.2. This workaround must
2896         // remain until the required Alembic version is changed for USD.
2897         //
2898         // The Alembic issue can be tracked here:
2899         // https://github.com/alembic/alembic/issues/129
2900         bool isPartition = false;
2901         try {
2902             isPartition = object.getSchema().getFaceExclusivity() == kFaceSetExclusive;
2903         }
2904         catch(const Alembic::Util::Exception&) {}
2905 
2906         if (isPartition)
2907         {
2908             return dst.Set(UsdGeomTokens->nonOverlapping);
2909         }
2910 
2911         return dst.Set(UsdGeomTokens->unrestricted);
2912     }
2913 };
2914 
2915 static
2916 TfToken
_ConvertCurveBasis(BasisType value)2917 _ConvertCurveBasis(BasisType value)
2918 {
2919     switch (value) {
2920     default:
2921     case kNoBasis:
2922     case kBezierBasis:
2923         return UsdGeomTokens->bezier;
2924     case kBsplineBasis:
2925         return UsdGeomTokens->bspline;
2926     case kCatmullromBasis:
2927         return UsdGeomTokens->catmullRom;
2928     case kHermiteBasis:
2929         TF_WARN(
2930             "'hermite' basis is no longer an allowed token for "
2931             "UsdGeomBasisCurves.");
2932         return UsdGeomTokens->hermite;
2933     case kPowerBasis:
2934         TF_WARN(
2935             "'power' basis is no longer an allowed token for "
2936             "UsdGeomBasisCurves.");
2937         return UsdGeomTokens->power;
2938     }
2939 }
2940 
2941 static
2942 TfToken
_ConvertCurveType(CurveType value)2943 _ConvertCurveType(CurveType value)
2944 {
2945     switch (value) {
2946     default:
2947     case kCubic:
2948         return UsdGeomTokens->cubic;
2949     case kLinear:
2950         return UsdGeomTokens->linear;
2951     }
2952 }
2953 
2954 static
2955 TfToken
_ConvertCurveWrap(CurvePeriodicity value)2956 _ConvertCurveWrap(CurvePeriodicity value)
2957 {
2958     switch (value) {
2959     default:
2960     case kNonPeriodic:
2961         return UsdGeomTokens->nonperiodic;
2962     case kPeriodic:
2963         return UsdGeomTokens->periodic;
2964     }
2965 }
2966 
2967 
2968 //
2969 // Object property readers
2970 //
2971 
2972 static
2973 void
_ReadGprim(_PrimReaderContext * context)2974 _ReadGprim(_PrimReaderContext* context)
2975 {
2976     // Add properties.
2977     context->AddProperty(
2978         UsdGeomTokens->extent,
2979         SdfValueTypeNames->Float3Array,
2980         _CopyBoundingBox(context->ExtractSchema(".selfBnds")));
2981 
2982     // Consume properties implicitly handled above.
2983     context->Extract(GeomBaseSchemaInfo::defaultName());
2984 }
2985 
2986 static
2987 void
_ReadArbGeomParams(_PrimReaderContext * context)2988 _ReadArbGeomParams(_PrimReaderContext* context)
2989 {
2990     // Add primvars.
2991     context->AddOutOfSchemaProperty(
2992         UsdAbcPropertyNames->primvars, context->ExtractSchema(".arbGeomParams"));
2993 }
2994 
2995 static
2996 void
_ReadUserProperties(_PrimReaderContext * context)2997 _ReadUserProperties(_PrimReaderContext* context)
2998 {
2999     // Add userProperties.
3000     context->AddOutOfSchemaProperty(
3001         UsdAbcPropertyNames->userProperties,
3002         context->ExtractSchema(".userProperties"));
3003 }
3004 
3005 static
3006 void
_ReadImageable(_PrimReaderContext * context)3007 _ReadImageable(_PrimReaderContext* context)
3008 {
3009     // Add properties.
3010     context->AddProperty(
3011         UsdGeomTokens->visibility,
3012         SdfValueTypeNames->Token,
3013         _CopyVisibility(context->Extract(kVisibilityPropertyName)));
3014 }
3015 
3016 static
3017 void
_ReadMayaColor(_PrimReaderContext * context)3018 _ReadMayaColor(_PrimReaderContext* context)
3019 {
3020     static const TfToken displayColor("primvars:displayColor");
3021 
3022     // Add properties.
3023     context->AddProperty(
3024         displayColor,
3025         SdfValueTypeNames->Color3fArray,
3026         _CopyAdskColor(context->ExtractSchema("adskDiffuseColor")));
3027 }
3028 
3029 static
3030 void
_ReadOther(_PrimReaderContext * context)3031 _ReadOther(_PrimReaderContext* context)
3032 {
3033     // Read every unextracted property to Usd using default converters.
3034     // This handles any property we don't have specific rules for.
3035     for (const auto& name : context->GetUnextractedNames()) {
3036         context->AddOutOfSchemaProperty(
3037             context->GetUsdName(name), context->Extract(name));
3038     }
3039 }
3040 
3041 template<class T, class UsdValueType>
3042 void
_ReadProperty(_PrimReaderContext * context,const char * name,TfToken propName,SdfValueTypeName typeName)3043 _ReadProperty(_PrimReaderContext* context, const char* name, TfToken propName, SdfValueTypeName typeName)
3044 {
3045     // Read a generic Alembic property and convert it to a USD property.
3046     // If the Alembic property is indexed, this will add both the values
3047     // property and the indices property, in order to preserve topology.
3048     auto prop = context->ExtractSchema(name);
3049     if (prop.Cast<T>().isIndexed()) {
3050         context->AddProperty(
3051             propName,
3052             typeName,
3053             _CopyGeneric<T, UsdValueType, false>(prop));
3054         context->AddProperty(
3055             TfToken(SdfPath::JoinIdentifier(propName, UsdGeomTokens->indices)),
3056             SdfValueTypeNames->IntArray,
3057             _CopyIndices<T>(prop));
3058     } else {
3059         context->AddProperty(
3060             propName,
3061             typeName,
3062             _CopyGeneric<T, UsdValueType>(prop));
3063     }
3064 }
3065 
3066 static
3067 void
_ReadOrientation(_PrimReaderContext * context)3068 _ReadOrientation(_PrimReaderContext* context)
3069 {
3070     AlembicProperty orientation =
3071         context->Extract(_AmdName(UsdGeomTokens->orientation));
3072     if (orientation.Cast<IStringProperty>().valid()) {
3073         context->AddProperty(
3074             UsdGeomTokens->orientation,
3075             SdfValueTypeNames->Token,
3076             _CopyOrientation(orientation));
3077     } else {
3078         // Alembic is effectively hardcoded to a left-handed orientation.
3079         // UsdGeomGprim's fallback is right-handed, so we need to provide
3080         // a value if none is authored.
3081         context->AddUniformProperty(
3082             UsdGeomTokens->orientation,
3083             SdfValueTypeNames->Token,
3084             _CopySynthetic(UsdGeomTokens->leftHanded));
3085     }
3086 }
3087 
3088 //
3089 // Object readers -- these set the prim type.
3090 //
3091 
3092 static
3093 void
_ReadUnknown(_PrimReaderContext * context)3094 _ReadUnknown(_PrimReaderContext* context)
3095 {
3096     // Set prim type.
3097     _PrimReaderContext::Prim& prim = context->GetPrim();
3098     prim.typeName =
3099         TfToken(context->GetObject().getMetaData().
3100                     get(_AmdName(SdfFieldKeys->TypeName)));
3101     if (prim.typeName.IsEmpty()) {
3102         // No type specified.  If we're a def then use Scope for lack of
3103         // anything better.
3104         if (prim.specifier == SdfSpecifierDef) {
3105             prim.typeName = UsdAbcPrimTypeNames->Scope;
3106         }
3107     }
3108 }
3109 
3110 static
3111 void
_ReadGeomBase(_PrimReaderContext * context)3112 _ReadGeomBase(_PrimReaderContext* context)
3113 {
3114     _ReadUnknown(context);
3115 
3116     // Add child properties under schema.
3117     context->SetSchema(GeomBaseSchemaInfo::defaultName());
3118 }
3119 
3120 static
3121 void
_ReadXform(_PrimReaderContext * context)3122 _ReadXform(_PrimReaderContext* context)
3123 {
3124     typedef IXform Type;
3125 
3126     // Wrap the object.
3127     if (!Type::matches(context->GetObject().getHeader())) {
3128         // Not of type Type.
3129         return;
3130     }
3131     Type object(context->GetObject(), kWrapExisting);
3132     Type::schema_type& schema = object.getSchema();
3133 
3134     // Add child properties under schema.
3135     context->SetSchema(Type::schema_type::info_type::defaultName());
3136 
3137     const index_t nSamples = _GetNumSamples(schema);
3138 
3139     // Error checking.
3140     for (index_t i = 0; i < nSamples; ++i) {
3141         if (!schema.getInheritsXforms(ISampleSelector(i))) {
3142             TF_WARN("Ignoring transform that doesn't inherit at "
3143                     "samples at time %f at <%s>",
3144                     schema.getTimeSampling()->getSampleTime(i),
3145                     context->GetPath().GetText());
3146             return;
3147         }
3148     }
3149 
3150     // Set prim type.
3151     context->GetPrim().typeName = UsdAbcPrimTypeNames->Xform;
3152 
3153     // Add properties.
3154     if (nSamples > 0) {
3155         // We could author individual component transforms here, just
3156         // as the transform is represented in alembic, but round-tripping
3157         // will be an issue because of the way the alembicWriter reads
3158         // transforms out of USD.
3159         //
3160         // For now, we're exporting the composed transform value, until
3161         // we figure out a solution to the round-tripping problem.
3162         //
3163         context->AddProperty(
3164             _tokens->xformOpTransform,
3165             SdfValueTypeNames->Matrix4d,
3166             _CopyXform(object));
3167 
3168         VtTokenArray opOrderVec(1);
3169         opOrderVec[0] = _tokens->xformOpTransform;
3170         context->AddUniformProperty(
3171             UsdGeomTokens->xformOpOrder,
3172             SdfValueTypeNames->TokenArray,
3173             _CopySynthetic(opOrderVec));
3174     }
3175 
3176     // Consume properties implicitly handled above.
3177     context->Extract(Type::schema_type::info_type::defaultName());
3178 }
3179 
3180 static
3181 void
_ReadPolyMesh(_PrimReaderContext * context)3182 _ReadPolyMesh(_PrimReaderContext* context)
3183 {
3184     typedef IPolyMesh Type;
3185 
3186     // Wrap the object.
3187     if (!Type::matches(context->GetObject().getHeader())) {
3188         // Not of type Type.
3189         return;
3190     }
3191 
3192     // Set prim type.
3193     context->GetPrim().typeName = UsdAbcPrimTypeNames->Mesh;
3194 
3195     // Add child properties under schema.
3196     context->SetSchema(Type::schema_type::info_type::defaultName());
3197 
3198     // Add properties.
3199     context->AddProperty(
3200         UsdGeomTokens->points,
3201         SdfValueTypeNames->Point3fArray,
3202         _CopyGeneric<IP3fArrayProperty, GfVec3f>(
3203             context->ExtractSchema("P"), kNoMatching));
3204     context->AddProperty(
3205         UsdGeomTokens->velocities,
3206         SdfValueTypeNames->Vector3fArray,
3207         _CopyGeneric<IV3fArrayProperty, GfVec3f>(
3208             context->ExtractSchema(".velocities")));
3209     context->AddProperty(
3210         UsdGeomTokens->normals,
3211         SdfValueTypeNames->Normal3fArray,
3212         _CopyGeneric<IN3fGeomParam, GfVec3f>(
3213             context->ExtractSchema("N")));
3214     context->AddProperty(
3215         UsdGeomTokens->faceVertexIndices,
3216         SdfValueTypeNames->IntArray,
3217         _CopyGeneric<IInt32ArrayProperty, int>(
3218             context->ExtractSchema(".faceIndices")));
3219     context->AddProperty(
3220         UsdGeomTokens->faceVertexCounts,
3221         SdfValueTypeNames->IntArray,
3222         _CopyGeneric<IInt32ArrayProperty, int>(
3223             context->ExtractSchema(".faceCounts")));
3224 
3225     // Read texture coordinates
3226     _ReadProperty<IV2fGeomParam, GfVec2f>(context, "uv", _GetUVPropertyName(), _GetUVTypeName());
3227 
3228     // Custom subdivisionScheme property.  Alembic doesn't have this since
3229     // the Alembic schema is PolyMesh.  Usd needs "none" as the scheme.
3230     context->AddUniformProperty(
3231         UsdGeomTokens->subdivisionScheme,
3232         SdfValueTypeNames->Token,
3233         _CopySynthetic(UsdGeomTokens->none));
3234 }
3235 
3236 static
3237 void
_ReadSubD(_PrimReaderContext * context)3238 _ReadSubD(_PrimReaderContext* context)
3239 {
3240     typedef ISubD Type;
3241 
3242     // Wrap the object.
3243     if (!Type::matches(context->GetObject().getHeader())) {
3244         // Not of type Type.
3245         return;
3246     }
3247 
3248     // Set prim type.
3249     context->GetPrim().typeName = UsdAbcPrimTypeNames->Mesh;
3250 
3251     // Add child properties under schema.
3252     context->SetSchema(Type::schema_type::info_type::defaultName());
3253 
3254     // Add properties.
3255     context->AddProperty(
3256         UsdGeomTokens->points,
3257         SdfValueTypeNames->Point3fArray,
3258         _CopyGeneric<IP3fArrayProperty, GfVec3f>(
3259             context->ExtractSchema("P"), kNoMatching));
3260     context->AddProperty(
3261         UsdGeomTokens->velocities,
3262         SdfValueTypeNames->Vector3fArray,
3263         _CopyGeneric<IV3fArrayProperty, GfVec3f>(
3264             context->ExtractSchema(".velocities")));
3265     context->AddProperty(
3266         UsdGeomTokens->faceVertexIndices,
3267         SdfValueTypeNames->IntArray,
3268         _CopyGeneric<IInt32ArrayProperty, int>(
3269             context->ExtractSchema(".faceIndices")));
3270     context->AddProperty(
3271         UsdGeomTokens->faceVertexCounts,
3272         SdfValueTypeNames->IntArray,
3273         _CopyGeneric<IInt32ArrayProperty, int>(
3274             context->ExtractSchema(".faceCounts")));
3275     context->AddUniformProperty(
3276         UsdGeomTokens->subdivisionScheme,
3277         SdfValueTypeNames->Token,
3278         _CopySubdivisionScheme(context->ExtractSchema(".scheme")));
3279     context->AddProperty(
3280         UsdGeomTokens->interpolateBoundary,
3281         SdfValueTypeNames->Token,
3282         _CopyInterpolateBoundary(
3283             context->ExtractSchema(".interpolateBoundary")));
3284     context->AddProperty(
3285         UsdGeomTokens->faceVaryingLinearInterpolation,
3286         SdfValueTypeNames->Token,
3287         _CopyFaceVaryingInterpolateBoundary(
3288             context->ExtractSchema(".faceVaryingInterpolateBoundary")));
3289     context->AddProperty(
3290         UsdGeomTokens->holeIndices,
3291         SdfValueTypeNames->IntArray,
3292         _CopyGeneric<IInt32ArrayProperty, int>(
3293             context->ExtractSchema(".holes")));
3294     context->AddProperty(
3295         UsdGeomTokens->cornerIndices,
3296         SdfValueTypeNames->IntArray,
3297         _CopyGeneric<IInt32ArrayProperty, int>(
3298             context->ExtractSchema(".cornerIndices")));
3299     context->AddProperty(
3300         UsdGeomTokens->cornerSharpnesses,
3301         SdfValueTypeNames->FloatArray,
3302         _CopyGeneric<IFloatArrayProperty, float>(
3303             context->ExtractSchema(".cornerSharpnesses")));
3304     context->AddProperty(
3305         UsdGeomTokens->creaseIndices,
3306         SdfValueTypeNames->IntArray,
3307         _CopyGeneric<IInt32ArrayProperty, int>(
3308             context->ExtractSchema(".creaseIndices")));
3309     context->AddProperty(
3310         UsdGeomTokens->creaseLengths,
3311         SdfValueTypeNames->IntArray,
3312         _CopyGeneric<IInt32ArrayProperty, int>(
3313             context->ExtractSchema(".creaseLengths")));
3314     context->AddProperty(
3315         UsdGeomTokens->creaseSharpnesses,
3316         SdfValueTypeNames->FloatArray,
3317         _CopyGeneric<IFloatArrayProperty, float>(
3318             context->ExtractSchema(".creaseSharpnesses")));
3319 
3320     // Read texture coordinates
3321     _ReadProperty<IV2fGeomParam, GfVec2f>(context, "uv", _GetUVPropertyName(), _GetUVTypeName());
3322 }
3323 
3324 static
3325 void
_ReadFaceSet(_PrimReaderContext * context)3326 _ReadFaceSet(_PrimReaderContext* context)
3327 {
3328     typedef IFaceSet Type;
3329 
3330     // Wrap the object.
3331     if (!Type::matches(context->GetObject().getHeader())) {
3332         // Not of type Type.
3333         return;
3334     }
3335 
3336     // A FaceSet must be a child of another Alembic object.
3337     if (!context->GetObject().getParent()) {
3338         // No parent exists.
3339         return;
3340     }
3341 
3342     Type object(context->GetObject(), kWrapExisting);
3343 
3344     // Add child properties under schema.
3345     context->SetSchema(Type::schema_type::info_type::defaultName());
3346 
3347     // Set prim type.
3348     context->GetPrim().typeName = UsdAbcPrimTypeNames->GeomSubset;
3349 
3350     context->AddProperty(
3351         UsdGeomTokens->indices,
3352         SdfValueTypeNames->IntArray,
3353         _CopyGeneric<IInt32ArrayProperty, int>(
3354             context->ExtractSchema(".faces")));
3355     context->AddUniformProperty(
3356         UsdGeomTokens->elementType,
3357         SdfValueTypeNames->Token,
3358         _CopySynthetic(UsdGeomTokens->face));
3359 
3360     context->AddUniformProperty(
3361         UsdGeomTokens->familyName,
3362         SdfValueTypeNames->Token,
3363         _CopySynthetic(UsdAbcPropertyNames->defaultFamilyName));
3364 
3365     _PrimReaderContext parentPrimContext = context->GetParentContext();
3366 
3367     parentPrimContext.AddUniformProperty(
3368         UsdAbcPropertyNames->defaultFamilyTypeAttributeName,
3369         SdfValueTypeNames->Token,
3370         _CopyFaceSetFamilyType(object));
3371 
3372     // Consume properties implicitly handled above.
3373     context->Extract(Type::schema_type::info_type::defaultName());
3374 }
3375 
3376 static
3377 void
_ReadCurves(_PrimReaderContext * context)3378 _ReadCurves(_PrimReaderContext* context)
3379 {
3380     typedef ICurves Type;
3381 
3382     // Wrap the object.
3383     if (!Type::matches(context->GetObject().getHeader())) {
3384         // Not of type Type.
3385         return;
3386     }
3387 
3388     // Add child properties under schema.
3389     context->SetSchema(Type::schema_type::info_type::defaultName());
3390 
3391     // An Alembic can animate the curve type, basis and periodicity but
3392     // Usd cannot.  We'll simply take the first sample's values and use
3393     // them for all samples.  We also extract and ignore the basis, type
3394     // and periodicity property (they're all packed into one property).
3395     Type::schema_type::Sample sample;
3396     if (!context->GetSample<Type>(sample, ISampleSelector())) {
3397         // Doesn't appear to have the right type.
3398         return;
3399     }
3400     (void)context->ExtractSchema("curveBasisAndType");
3401 
3402     // Set prim type.  This depends on the CurveType of the curve.
3403     if (sample.getType() == kVariableOrder){
3404         context->GetPrim().typeName = UsdAbcPrimTypeNames->NurbsCurves;
3405         context->AddProperty(
3406             UsdGeomTokens->order,
3407             SdfValueTypeNames->IntArray,
3408             _CopyGeneric<IInt32ArrayProperty, int>(
3409                 context->ExtractSchema(".orders")));
3410         context->AddProperty(
3411             UsdGeomTokens->knots,
3412             SdfValueTypeNames->DoubleArray,
3413             _CopyGeneric<IFloatArrayProperty, double>(
3414                 context->ExtractSchema(".knots")));
3415     } else if ((sample.getType() == kCubic) &&
3416                (sample.getBasis() == kHermiteBasis)) {
3417         context->GetPrim().typeName = UsdAbcPrimTypeNames->HermiteCurves;
3418         if (_ConvertCurveWrap(sample.getWrap()) != UsdGeomTokens->nonperiodic) {
3419             TF_WARN("Wrap mode is not supported for cubic hermite curves: '%s'",
3420                     context->GetPath().GetText());
3421         }
3422         if (_CopyGeneric<IV3fArrayProperty, GfVec3f>(
3423                 context->ExtractSchema(".velocities"))(_IsValidTag())) {
3424             TF_WARN(
3425                 "Velocities are not supported for cubic hermite curves: '%s'",
3426                 context->GetPath().GetText());
3427         }
3428         // The Usd representation separates points and tangents
3429         // The Alembic representation interleaves them as (P0, T0, ... Pn, Tn)
3430         context->AddProperty(
3431             UsdGeomTokens->points,
3432             SdfValueTypeNames->Point3fArray,
3433             _CopyHermitePoints(context->ExtractSchema("P")));
3434         context->AddProperty(
3435             UsdGeomTokens->tangents,
3436             SdfValueTypeNames->Vector3fArray,
3437             _CopyHermiteTangents(context->ExtractSchema("P")));
3438     } else {
3439         context->GetPrim().typeName = UsdAbcPrimTypeNames->BasisCurves;
3440         context->AddUniformProperty(
3441             UsdGeomTokens->type,
3442             SdfValueTypeNames->Token,
3443             _CopySynthetic(_ConvertCurveType(sample.getType())));
3444         context->AddUniformProperty(
3445             UsdGeomTokens->basis,
3446             SdfValueTypeNames->Token,
3447             _CopySynthetic(_ConvertCurveBasis(sample.getBasis())));
3448         context->AddUniformProperty(
3449             UsdGeomTokens->wrap,
3450             SdfValueTypeNames->Token,
3451             _CopySynthetic(_ConvertCurveWrap(sample.getWrap())));
3452     }
3453 
3454     if ((context->GetPrim().typeName == UsdAbcPrimTypeNames->BasisCurves) ||
3455         (context->GetPrim().typeName == UsdAbcPrimTypeNames->NurbsCurves)){
3456         context->AddProperty(
3457             UsdGeomTokens->points,
3458             SdfValueTypeNames->Point3fArray,
3459             _CopyGeneric<IP3fArrayProperty, GfVec3f>(
3460                 context->ExtractSchema("P"), kNoMatching));
3461         context->AddProperty(
3462             UsdGeomTokens->velocities,
3463             SdfValueTypeNames->Vector3fArray,
3464             _CopyGeneric<IV3fArrayProperty, GfVec3f>(
3465                 context->ExtractSchema(".velocities")));
3466     }
3467 
3468     context->AddProperty(
3469         UsdGeomTokens->normals,
3470         SdfValueTypeNames->Normal3fArray,
3471         _CopyGeneric<IN3fGeomParam, GfVec3f>(
3472             context->ExtractSchema("N")));
3473     context->AddProperty(
3474         UsdGeomTokens->curveVertexCounts,
3475         SdfValueTypeNames->IntArray,
3476         _CopyGeneric<IInt32ArrayProperty, int>(
3477             context->ExtractSchema("nVertices")));
3478     context->AddProperty(
3479         UsdGeomTokens->widths,
3480         SdfValueTypeNames->FloatArray,
3481         _CopyGeneric<IFloatGeomParam, float>(
3482             context->ExtractSchema("width")));
3483 }
3484 
3485 static
3486 void
_ReadPoints(_PrimReaderContext * context)3487 _ReadPoints(_PrimReaderContext* context)
3488 {
3489     typedef IPoints Type;
3490 
3491     // Wrap the object.
3492     if (!Type::matches(context->GetObject().getHeader())) {
3493         // Not of type Type.
3494         return;
3495     }
3496 
3497     // Set prim type.
3498     context->GetPrim().typeName = UsdAbcPrimTypeNames->Points;
3499 
3500     // Add child properties under schema.
3501     context->SetSchema(Type::schema_type::info_type::defaultName());
3502 
3503     // Add properties.
3504     context->AddProperty(
3505         UsdGeomTokens->points,
3506         SdfValueTypeNames->Point3fArray,
3507         _CopyGeneric<IP3fArrayProperty, GfVec3f>(
3508             context->ExtractSchema("P"), kNoMatching));
3509     context->AddProperty(
3510         UsdGeomTokens->velocities,
3511         SdfValueTypeNames->Vector3fArray,
3512         _CopyGeneric<IV3fArrayProperty, GfVec3f>(
3513             context->ExtractSchema(".velocities")));
3514     context->AddProperty(
3515         UsdGeomTokens->widths,
3516         SdfValueTypeNames->FloatArray,
3517         _CopyGeneric<IFloatGeomParam, float>(
3518             context->ExtractSchema(".widths")));
3519     context->AddProperty(
3520         UsdGeomTokens->ids,
3521         SdfValueTypeNames->Int64Array,
3522         _CopyGeneric<IUInt64ArrayProperty, int64_t>(
3523             context->ExtractSchema(".pointIds")));
3524 }
3525 
3526 static
3527 void
_ReadCameraParameters(_PrimReaderContext * context)3528 _ReadCameraParameters(_PrimReaderContext* context)
3529 {
3530     typedef ICamera Type;
3531 
3532     // Wrap the object.
3533     if (!Type::matches(context->GetObject().getHeader())) {
3534         // Not of type Type.
3535         return;
3536     }
3537     Type object(context->GetObject(), kWrapExisting);
3538 
3539     // Set prim type.
3540     context->GetPrim().typeName = UsdAbcPrimTypeNames->Camera;
3541 
3542     // Add child properties under schema.
3543     context->SetSchema(
3544         Type::schema_type::info_type::defaultName());
3545 
3546     // Add the minimal set of properties to set up the
3547     // camera frustum.
3548     context->AddProperty(
3549         UsdGeomTokens->focalLength,
3550         SdfValueTypeNames->Float,
3551         _CopyCameraFocalLength(object));
3552     context->AddProperty(
3553         UsdGeomTokens->horizontalAperture,
3554         SdfValueTypeNames->Float,
3555         _CopyCameraHorizontalAperture(object));
3556     context->AddProperty(
3557         UsdGeomTokens->verticalAperture,
3558         SdfValueTypeNames->Float,
3559         _CopyCameraVerticalAperture(object));
3560     context->AddProperty(
3561         UsdGeomTokens->horizontalApertureOffset,
3562         SdfValueTypeNames->Float,
3563         _CopyCameraHorizontalApertureOffset(object));
3564     context->AddProperty(
3565         UsdGeomTokens->verticalApertureOffset,
3566         SdfValueTypeNames->Float,
3567         _CopyCameraVerticalApertureOffset(object));
3568     context->AddProperty(
3569         UsdGeomTokens->clippingRange,
3570         SdfValueTypeNames->Float2,
3571         _CopyCameraClippingRange(object));
3572 
3573     // Extract all other alembic camera properties so that they don't show up
3574     // in USD.
3575     // In particular, alembic camera back xforms are backed out and should
3576     // not show up in USD.
3577     context->Extract(Type::schema_type::info_type::defaultName());
3578 }
3579 
3580 static
3581 _ReaderContext::Ordering
_GetOrderingMetadata(const MetaData & alembicMetadata,const TfToken & field)3582 _GetOrderingMetadata(
3583     const MetaData& alembicMetadata,
3584     const TfToken& field)
3585 {
3586     const std::string& value = alembicMetadata.get(_AmdName(field));
3587     if (!value.empty()) {
3588         const std::vector<std::string> names = TfStringTokenize(value, " []");
3589         if (!names.empty()) {
3590             return _ReaderContext::Ordering(
3591                         TfTokenVector(names.begin(), names.end()));
3592         }
3593     }
3594     return _ReaderContext::Ordering();
3595 }
3596 
3597 static
3598 void
_GetPrimMetadata(const MetaData & metadata,_ReaderContext::Prim & prim)3599 _GetPrimMetadata(const MetaData& metadata, _ReaderContext::Prim& prim)
3600 {
3601     // def/over.
3602     if (metadata.get(_AmdName(SdfFieldKeys->Specifier)) == "over") {
3603         prim.specifier = SdfSpecifierOver;
3604     }
3605     else {
3606         prim.specifier = SdfSpecifierDef;
3607     }
3608 
3609     // Other metadata.
3610     _GetBoolMetadata(metadata, prim.metadata, SdfFieldKeys->Active);
3611     _GetBoolMetadata(metadata, prim.metadata, SdfFieldKeys->Hidden);
3612     _GetStringMetadata(metadata, prim.metadata, SdfFieldKeys->DisplayGroup);
3613     _GetStringMetadata(metadata, prim.metadata, SdfFieldKeys->Documentation);
3614     _GetTokenMetadata(metadata, prim.metadata, SdfFieldKeys->Kind);
3615 
3616     // Add name children ordering.
3617     prim.primOrdering =
3618         _GetOrderingMetadata(metadata, SdfFieldKeys->PrimOrder);
3619 
3620     // Add property ordering.
3621     prim.propertyOrdering =
3622         _GetOrderingMetadata(metadata, SdfFieldKeys->PropertyOrder);
3623 }
3624 
3625 static
3626 std::string
_ComputeSchemaName(const _ReaderContext & context,const IObject & object)3627 _ComputeSchemaName(
3628     const _ReaderContext& context,
3629     const IObject& object)
3630 {
3631     // Special case where we stored the type.  Note that we can't assume
3632     // that this is accurate.  For example, this might say its an Xform
3633     // but we must be prepared for it not to actually be an Alembic Xform.
3634     std::string value =
3635         object.getMetaData().get(_AmdName(SdfFieldKeys->TypeName));
3636     if (!value.empty()) {
3637         return value;
3638     }
3639 
3640     // General case.
3641     const std::string schema = object.getMetaData().get("schema");
3642 
3643     // Special cases.  If there's no schema try the base type.
3644     if (schema.empty()) {
3645         return object.getMetaData().get("schemaBaseType");
3646     }
3647 
3648     return schema;
3649 }
3650 
3651 template <typename TYPE>
3652 static
3653 ICompoundProperty
_GetSchemaProperty(const IObject & object)3654 _GetSchemaProperty(const IObject& object)
3655 {
3656     return ICompoundProperty(object.getProperties(),
3657                              TYPE::schema_type::info_type::defaultName(),
3658                              ErrorHandler::kQuietNoopPolicy);
3659 }
3660 
3661 static
3662 std::string
_ReadPrim(_ReaderContext & context,const IObject & object,const SdfPath & parentPath,std::set<std::string> * usedSiblingNames)3663 _ReadPrim(
3664     _ReaderContext& context,
3665     const IObject& object,
3666     const SdfPath& parentPath,
3667     std::set<std::string>* usedSiblingNames)
3668 {
3669     // Choose the name.
3670     std::string name = _CleanName(object.getName(), " _", *usedSiblingNames,
3671                                   _AlembicFixName(),
3672                                   &SdfPath::IsValidIdentifier);
3673     usedSiblingNames->insert(name);
3674     SdfPath path = parentPath.AppendChild(TfToken(name));
3675 
3676     // Compute the schema name.
3677     const std::string schemaName = _ComputeSchemaName(context, object);
3678 
3679     // Handle non-instances.
3680     _ReaderContext::Prim* instance = nullptr;
3681     if (!context.IsInstance(object)) {
3682         // Combine geom with parent if parent is a transform.  There are
3683         // several cases where we want to bail out and, rather than use a
3684         // huge if statement or deep if nesting, we'll use do/while and
3685         // break to do it.
3686         do {
3687             if (!TfGetEnvSetting(USD_ABC_XFORM_PRIM_COLLAPSE)) {
3688                 // Transform collapse is specified as unwanted behavior
3689                 break;
3690             }
3691             // Parent has to be a transform.
3692             IObject parent = object.getParent();
3693             if (!IXform::matches(parent.getHeader())) {
3694                 break;
3695             }
3696             ICompoundProperty parentProperties =
3697                 _GetSchemaProperty<IXform>(parent);
3698             if (!parentProperties.valid()) {
3699                 break;
3700             }
3701 
3702             // The parent must not be the root of an instance.
3703             if (context.IsInstance(parent)) {
3704                 break;
3705             }
3706 
3707             // This object must be an IGeomBase or ICamera.
3708             // XXX: May want to be more selective here.
3709             ICompoundProperty objectProperties;
3710             if (IGeomBase::matches(object.getMetaData())) {
3711                 objectProperties = _GetSchemaProperty<IGeomBaseObject>(object);
3712             }
3713             else if (ICamera::matches(object.getMetaData(),
3714                                      kSchemaTitleMatching)) {
3715                 objectProperties = _GetSchemaProperty<ICamera>(object);
3716             }
3717             if (!objectProperties.valid()) {
3718                 break;
3719             }
3720 
3721             // We can't merge .arbGeomParams and .userProperties so
3722             // bail if either are in both this object and the parent.
3723             if (objectProperties.getPropertyHeader(".arbGeomParams") &&
3724                 parentProperties.getPropertyHeader(".arbGeomParams")) {
3725                 break;
3726             }
3727             if (objectProperties.getPropertyHeader(".userProperties") &&
3728                 parentProperties.getPropertyHeader(".userProperties")) {
3729                 break;
3730             }
3731 
3732             // We can combine!  Cache into our parent's entry.
3733             path = parentPath;
3734 
3735             // Don't add this object to the parent's children.
3736             name.clear();
3737         } while (false);
3738     }
3739 
3740     // If this is an instance then we create a prim at the path and
3741     // give it a reference to the prototype.  Then we change the path to
3742     // be that of the prototype and continue traversal.  This puts the
3743     // entire prototype hierarchy under the new path and puts a simple
3744     // prim with nothing but a reference at the old path.
3745     else {
3746         instance = &context.AddInstance(path, object);
3747         if (!instance->prototype.IsEmpty()) {
3748             path = instance->prototype;
3749         }
3750         else {
3751             instance = nullptr;
3752             const std::string prototypePath =
3753                 object.isInstanceRoot()
3754                     ? IObject(object).instanceSourcePath()
3755                     : object.getFullName();
3756             TF_CODING_ERROR(
3757                 "Instance %s has no prototype at %s.",
3758                 object.getFullName().c_str(),
3759                 prototypePath.c_str());
3760             // Continue and we'll simply expand the instance.
3761         }
3762     }
3763 
3764     // At this point if instance != nullptr then we're instancing,
3765     // path points to the prototype, and instance points to the instance
3766     // prim's cache.
3767 
3768     // If the instance source was promoted then we need to copy the
3769     // prim's metadata and properties to the instance.  But we don't
3770     // need quite everything because the prototype will supply some of it.
3771     // For simplicity, we copy all data as usual then discard what we
3772     // don't want.
3773     if (instance && instance->promoted) {
3774         // Read the metadata.
3775         _GetPrimMetadata(object.getMetaData(), *instance);
3776 
3777         // Read the properties.  Reconstruct the instance path since we've
3778         // changed path to point at the prototype.
3779         const SdfPath instancePath = parentPath.AppendChild(TfToken(name));
3780         _PrimReaderContext primContext(context, object, instancePath);
3781         for (const auto& reader : context.GetSchema().GetPrimReaders(schemaName)) {
3782             TRACE_SCOPE("UsdAbc_AlembicDataReader:_ReadPrim");
3783             reader(&primContext);
3784         }
3785 
3786         // Discard name children ordering since we don't have any name
3787         // children (except via the prototype reference).
3788         instance->primOrdering = boost::none;
3789     }
3790 
3791     // Get the prim cache.  If instance is true then prim is the prototype,
3792     // otherwise it's a non-instanced prim or a descendant of a prototype.
3793     _ReaderContext::Prim& prim = context.AddPrim(path);
3794 
3795     // If we're instancing but the prototype prim cache already has a type
3796     // name then we've already found a previous instance of this prototype
3797     // and we've already traversed the prototype once.  Don't traverse a
3798     // prototype again.
3799     if (!instance || prim.typeName.IsEmpty()) {
3800         // Add prim metadata.
3801         _GetPrimMetadata(object.getMetaData(), prim);
3802 
3803 #ifdef USDABC_ALEMBIC_DEBUG
3804         fprintf(stdout, "%*s%s%s%s \"%s\" { # %s, %s\n",
3805                 2 * (int)(path.GetPathElementCount() - 1), "",
3806                 prim.specifier == SdfSpecifierOver ? "over" : "def",
3807                 prim.typeName.IsEmpty() ? "" : " ",
3808                 prim.typeName.GetText(),
3809                 name.empty() ? "<merge-with-parent>" : name.c_str(),
3810                 schemaName.c_str(), object.getName().c_str());
3811 #endif
3812 
3813         if (path != SdfPath::AbsoluteRootPath()) {
3814             // Read the properties.
3815             _PrimReaderContext primContext(context, object, path);
3816             for (const auto& reader : context.GetSchema().GetPrimReaders(schemaName)) {
3817                 TRACE_SCOPE("UsdAbc_AlembicDataReader:_ReadPrim");
3818                 reader(&primContext);
3819             }
3820         }
3821 
3822         // Recurse.
3823         _ReadPrimChildren(context, object, path, prim);
3824 
3825 #ifdef USDABC_ALEMBIC_DEBUG
3826         fprintf(stdout, "%*s}\n",
3827                 2 * (int)(path.GetPathElementCount() - 1), "");
3828 #endif
3829 
3830         // If the instance source was promoted then we don't need or want
3831         // any of the instance source's properties on the prototype since each
3832         // Usd instance will have its own.  We also don't want the prototype
3833         // to have most metadata for the same reason.  For the sake of
3834         // simplicity of the code, we already copied all of that info so
3835         // we'll discard it now.
3836         if (instance && instance->promoted) {
3837             // prim is the prototype.
3838             prim.properties.clear();
3839             prim.propertyOrdering = boost::none;
3840             prim.metadata.clear();
3841             prim.propertiesCache.clear();
3842         }
3843 
3844         // If this is a prototype then make it an over.
3845         if (instance) {
3846             prim.specifier = SdfSpecifierOver;
3847         }
3848     }
3849 
3850     // Modify the metadata for an instance.  We wait until now because we
3851     // want to get the prototype's type name.
3852     if (instance) {
3853         instance->typeName  = prim.typeName;
3854         instance->specifier = SdfSpecifierDef;
3855     }
3856 
3857     return name;
3858 }
3859 
3860 static
3861 void
_ReadPrimChildren(_ReaderContext & context,const IObject & object,const SdfPath & path,_ReaderContext::Prim & prim)3862 _ReadPrimChildren(
3863     _ReaderContext& context,
3864     const IObject& object,
3865     const SdfPath& path,
3866     _ReaderContext::Prim& prim)
3867 {
3868     // Collect children names.  By prepopulating usedNames we ensure that
3869     // the child with the valid name gets its name even if a child with a
3870     // lower index has a name that mangles to the valid name.
3871     std::set<std::string> usedNames;
3872     for (size_t i = 0, n = object.getNumChildren(); i != n; ++i) {
3873         usedNames.insert(object.getChildHeader(i).getName());
3874     }
3875 
3876     // Read the children.
3877     for (size_t i = 0, n = object.getNumChildren(); i != n; ++i) {
3878         IObject child(object, object.getChildHeader(i).getName());
3879         const std::string childName =
3880             _ReadPrim(context, child, path, &usedNames);
3881         if (!childName.empty()) {
3882             prim.children.push_back(TfToken(childName));
3883         }
3884     }
3885 }
3886 
3887 // ----------------------------------------------------------------------------
3888 
3889 //
3890 // Schema builder
3891 //
3892 
3893 struct _ReaderSchemaBuilder {
3894     _ReaderSchema schema;
3895 
3896     _ReaderSchemaBuilder();
3897 };
3898 
_ReaderSchemaBuilder()3899 _ReaderSchemaBuilder::_ReaderSchemaBuilder()
3900 {
3901     schema.AddType(GeomBaseSchemaInfo::title())
3902         .AppendReader(_ReadGeomBase)
3903         .AppendReader(_ReadMayaColor)
3904         .AppendReader(_ReadGprim)
3905         .AppendReader(_ReadImageable)
3906         .AppendReader(_ReadArbGeomParams)
3907         .AppendReader(_ReadUserProperties)
3908         .AppendReader(_ReadOther)
3909         ;
3910     schema.AddType(XformSchemaInfo::title())
3911         .AppendReader(_ReadXform)
3912         .AppendReader(_ReadImageable)
3913         .AppendReader(_ReadArbGeomParams)
3914         .AppendReader(_ReadUserProperties)
3915         .AppendReader(_ReadOther)
3916         ;
3917     schema.AddType(SubDSchemaInfo::title())
3918         .AppendReader(_ReadOrientation)
3919         .AppendReader(_ReadSubD)
3920         .AppendReader(_ReadMayaColor)
3921         .AppendReader(_ReadGprim)
3922         .AppendReader(_ReadImageable)
3923         .AppendReader(_ReadArbGeomParams)
3924         .AppendReader(_ReadUserProperties)
3925         .AppendReader(_ReadOther)
3926         ;
3927     schema.AddType(PolyMeshSchemaInfo::title())
3928         .AppendReader(_ReadOrientation)
3929         .AppendReader(_ReadPolyMesh)
3930         .AppendReader(_ReadMayaColor)
3931         .AppendReader(_ReadGprim)
3932         .AppendReader(_ReadImageable)
3933         .AppendReader(_ReadArbGeomParams)
3934         .AppendReader(_ReadUserProperties)
3935         .AppendReader(_ReadOther)
3936         ;
3937     schema.AddType(FaceSetSchemaInfo::title())
3938         .AppendReader(_ReadFaceSet)
3939         ;
3940     schema.AddType(CurvesSchemaInfo::title())
3941         .AppendReader(_ReadOrientation)
3942         .AppendReader(_ReadCurves)
3943         .AppendReader(_ReadMayaColor)
3944         .AppendReader(_ReadGprim)
3945         .AppendReader(_ReadImageable)
3946         .AppendReader(_ReadArbGeomParams)
3947         .AppendReader(_ReadUserProperties)
3948         .AppendReader(_ReadOther)
3949         ;
3950     schema.AddType(PointsSchemaInfo::title())
3951         .AppendReader(_ReadOrientation)
3952         .AppendReader(_ReadPoints)
3953         .AppendReader(_ReadMayaColor)
3954         .AppendReader(_ReadGprim)
3955         .AppendReader(_ReadImageable)
3956         .AppendReader(_ReadArbGeomParams)
3957         .AppendReader(_ReadUserProperties)
3958         .AppendReader(_ReadOther)
3959         ;
3960     schema.AddType(CameraSchemaInfo::title())
3961         .AppendReader(_ReadCameraParameters)
3962         .AppendReader(_ReadArbGeomParams)
3963         .AppendReader(_ReadUserProperties)
3964         .AppendReader(_ReadOther)
3965         ;
3966 
3967     // This handles overs with no type and any unknown prim type.
3968     schema.AddFallbackType()
3969         .AppendReader(_ReadGeomBase)        // Assume GeomBase.
3970         .AppendReader(_ReadMayaColor)
3971         .AppendReader(_ReadGprim)
3972         .AppendReader(_ReadImageable)
3973         .AppendReader(_ReadArbGeomParams)
3974         .AppendReader(_ReadUserProperties)
3975         .AppendReader(_ReadOther)
3976         ;
3977 }
3978 
3979 } // anonymous namespace
3980 
3981 static
3982 const _ReaderSchema&
_GetSchema()3983 _GetSchema()
3984 {
3985     static _ReaderSchemaBuilder builder;
3986     return builder.schema;
3987 }
3988 
3989 //
3990 // UsdAbc_AlembicDataReader::TimeSamples
3991 //
3992 
3993 
TimeSamples()3994 UsdAbc_AlembicDataReader::TimeSamples::TimeSamples()
3995 {
3996     // Do nothing
3997 }
3998 
TimeSamples(const std::vector<double> & times)3999 UsdAbc_AlembicDataReader::TimeSamples::TimeSamples(
4000     const std::vector<double>& times) :
4001     _times(times)
4002 {
4003     // Do nothing
4004 }
4005 
4006 void
Swap(TimeSamples & other)4007 UsdAbc_AlembicDataReader::TimeSamples::Swap(TimeSamples& other)
4008 {
4009     _times.swap(other._times);
4010 }
4011 
4012 bool
IsEmpty() const4013 UsdAbc_AlembicDataReader::TimeSamples::IsEmpty() const
4014 {
4015     return _times.empty();
4016 }
4017 
4018 size_t
GetSize() const4019 UsdAbc_AlembicDataReader::TimeSamples::GetSize() const
4020 {
4021     return _times.size();
4022 }
4023 
4024 UsdAbc_TimeSamples
GetTimes() const4025 UsdAbc_AlembicDataReader::TimeSamples::GetTimes() const
4026 {
4027     return UsdAbc_TimeSamples(_times.begin(), _times.end());
4028 }
4029 
4030 double
operator [](size_t index) const4031 UsdAbc_AlembicDataReader::TimeSamples::operator[](size_t index) const
4032 {
4033     return _times[index];
4034 }
4035 
4036 void
AddTo(UsdAbc_TimeSamples * samples) const4037 UsdAbc_AlembicDataReader::TimeSamples::AddTo(UsdAbc_TimeSamples* samples) const
4038 {
4039     samples->insert(_times.begin(), _times.end());
4040 }
4041 
4042 bool
FindIndex(double usdTime,Index * index) const4043 UsdAbc_AlembicDataReader::TimeSamples::FindIndex(double usdTime,Index* index) const
4044 {
4045     _UsdTimeCodes::const_iterator i =
4046         std::lower_bound(_times.begin(), _times.end(), usdTime);
4047     if (i == _times.end() || *i != usdTime) {
4048         return false;
4049     }
4050     else {
4051         *index = std::distance(_times.begin(), i);
4052         return true;
4053     }
4054 }
4055 
4056 template <class T>
4057 static
4058 typename std::vector<T>::const_iterator
UsdAbc_lower_bound(const std::vector<T> & c,T x)4059 UsdAbc_lower_bound(const std::vector<T>& c, T x)
4060 {
4061     return std::lower_bound(c.begin(), c.end(), x);
4062 }
4063 
4064 template <class T>
4065 static
4066 typename std::set<T>::const_iterator
UsdAbc_lower_bound(const std::set<T> & c,T x)4067 UsdAbc_lower_bound(const std::set<T>& c, T x)
4068 {
4069     return c.lower_bound(x);
4070 }
4071 
4072 template <class T>
4073 bool
Bracket(const T & samples,double usdTime,double * tLower,double * tUpper)4074 UsdAbc_AlembicDataReader::TimeSamples::Bracket(
4075     const T& samples, double usdTime,
4076     double* tLower, double* tUpper)
4077 {
4078     if (samples.empty()) {
4079         return false;
4080     }
4081 
4082     typename T::const_iterator i = UsdAbc_lower_bound(samples, usdTime);
4083     if (i == samples.end()) {
4084         // Past last sample.
4085         *tLower = *tUpper = *--i;
4086     }
4087     else if (i == samples.begin() || *i == usdTime) {
4088         // Before first sample or at a sample.
4089         *tLower = *tUpper = *i;
4090     }
4091     else {
4092         // Bracket a sample.
4093         *tUpper = *i;
4094         *tLower = *--i;
4095     }
4096     return true;
4097 }
4098 
4099 // Instantiate for UsdAbc_TimeSamples.
4100 template
4101 bool
4102 UsdAbc_AlembicDataReader::TimeSamples::Bracket(
4103     const UsdAbc_TimeSamples& samples, double usdTime,
4104     double* tLower, double* tUpper);
4105 
4106 bool
Bracket(double usdTime,double * tLower,double * tUpper) const4107 UsdAbc_AlembicDataReader::TimeSamples::Bracket(
4108     double usdTime, double* tLower, double* tUpper) const
4109 {
4110     return Bracket(_times, usdTime, tLower, tUpper);
4111 }
4112 
4113 //
4114 // UsdAbc_AlembicDataReader
4115 //
4116 
4117 class UsdAbc_AlembicDataReaderImpl : public _ReaderContext { };
4118 
UsdAbc_AlembicDataReader()4119 UsdAbc_AlembicDataReader::UsdAbc_AlembicDataReader() :
4120     _impl(new UsdAbc_AlembicDataReaderImpl)
4121 {
4122     _impl->SetSchema(&_GetSchema());
4123 }
4124 
~UsdAbc_AlembicDataReader()4125 UsdAbc_AlembicDataReader::~UsdAbc_AlembicDataReader()
4126 {
4127     Close();
4128 }
4129 
4130 bool
Open(const std::string & filePath,const SdfFileFormat::FileFormatArguments & args)4131 UsdAbc_AlembicDataReader::Open(const std::string& filePath,
4132                                const SdfFileFormat::FileFormatArguments& args)
4133 {
4134     TRACE_FUNCTION();
4135 
4136     _errorLog.clear();
4137     try {
4138         if (_impl->Open(filePath, &_errorLog, args)) {
4139             return true;
4140         }
4141     }
4142     catch (std::exception& e) {
4143         _errorLog.append(e.what());
4144         _errorLog.append("\n");
4145     }
4146     return false;
4147 }
4148 
4149 void
Close()4150 UsdAbc_AlembicDataReader::Close()
4151 {
4152     _impl->Close();
4153 }
4154 
4155 std::string
GetErrors() const4156 UsdAbc_AlembicDataReader::GetErrors() const
4157 {
4158     return _errorLog;
4159 }
4160 
4161 void
SetFlag(const TfToken & flagName,bool set)4162 UsdAbc_AlembicDataReader::SetFlag(const TfToken& flagName, bool set)
4163 {
4164     _impl->SetFlag(flagName, set);
4165 }
4166 
4167 bool
HasSpec(const SdfPath & path) const4168 UsdAbc_AlembicDataReader::HasSpec(const SdfPath& path) const
4169 {
4170     return _impl->HasSpec(path);
4171 }
4172 
4173 SdfSpecType
GetSpecType(const SdfPath & path) const4174 UsdAbc_AlembicDataReader::GetSpecType(const SdfPath& path) const
4175 {
4176     return _impl->GetSpecType(path);
4177 }
4178 
4179 bool
HasField(const SdfPath & path,const TfToken & fieldName,SdfAbstractDataValue * value) const4180 UsdAbc_AlembicDataReader::HasField(
4181     const SdfPath& path,
4182     const TfToken& fieldName,
4183     SdfAbstractDataValue* value) const
4184 {
4185     return _impl->HasField(path, fieldName, UsdAbc_AlembicDataAny(value));
4186 }
4187 
4188 bool
HasField(const SdfPath & path,const TfToken & fieldName,VtValue * value) const4189 UsdAbc_AlembicDataReader::HasField(
4190     const SdfPath& path,
4191     const TfToken& fieldName,
4192     VtValue* value) const
4193 {
4194     return _impl->HasField(path, fieldName, UsdAbc_AlembicDataAny(value));
4195 }
4196 
4197 bool
HasValue(const SdfPath & path,Index index,SdfAbstractDataValue * value) const4198 UsdAbc_AlembicDataReader::HasValue(
4199     const SdfPath& path,
4200     Index index,
4201     SdfAbstractDataValue* value) const
4202 {
4203     return _impl->HasValue(path, index, UsdAbc_AlembicDataAny(value));
4204 }
4205 
4206 bool
HasValue(const SdfPath & path,Index index,VtValue * value) const4207 UsdAbc_AlembicDataReader::HasValue(
4208     const SdfPath& path,
4209     Index index,
4210     VtValue* value) const
4211 {
4212     return _impl->HasValue(path, index, UsdAbc_AlembicDataAny(value));
4213 }
4214 
4215 void
VisitSpecs(const SdfAbstractData & owner,SdfAbstractDataSpecVisitor * visitor) const4216 UsdAbc_AlembicDataReader::VisitSpecs(
4217     const SdfAbstractData& owner,
4218     SdfAbstractDataSpecVisitor* visitor) const
4219 {
4220     return _impl->VisitSpecs(owner, visitor);
4221 }
4222 
4223 TfTokenVector
List(const SdfPath & path) const4224 UsdAbc_AlembicDataReader::List(const SdfPath& path) const
4225 {
4226     return _impl->List(path);
4227 }
4228 
4229 const std::set<double>&
ListAllTimeSamples() const4230 UsdAbc_AlembicDataReader::ListAllTimeSamples() const
4231 {
4232     return _impl->ListAllTimeSamples();
4233 }
4234 
4235 const UsdAbc_AlembicDataReader::TimeSamples&
ListTimeSamplesForPath(const SdfPath & path) const4236 UsdAbc_AlembicDataReader::ListTimeSamplesForPath(
4237     const SdfPath& path) const
4238 {
4239     return _impl->ListTimeSamplesForPath(path);
4240 }
4241 
4242 PXR_NAMESPACE_CLOSE_SCOPE
4243 
4244