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