1 //
2 // Copyright 2016 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 alembicData.cpp
25 
26 #include "pxr/pxr.h"
27 #include "pxr/usd/plugin/usdAbc/alembicData.h"
28 #include "pxr/usd/plugin/usdAbc/alembicReader.h"
29 #include "pxr/usd/plugin/usdAbc/alembicUtil.h"
30 #include "pxr/usd/plugin/usdAbc/alembicWriter.h"
31 #include "pxr/usd/sdf/schema.h"
32 #include "pxr/base/trace/trace.h"
33 #include "pxr/base/tf/envSetting.h"
34 #include "pxr/base/tf/fileUtils.h"
35 
36 PXR_NAMESPACE_OPEN_SCOPE
37 
38 
39 // Note: The Alembic translator has a few major parts.  Here's a
40 //       quick description.
41 //
42 //   Data type translation
43 //     Types and functions for describing Alembic data types and for
44 //     converting Usd <-> Alembic.
45 //
46 //   UsdAbc_AlembicDataConversion
47 //     A class for holding data type conversion tables.  It can convert
48 //     Alembic properties to Usd values and vice versa.  It does not
49 //     register any converters, it just tabulates them.  This hopefully
50 //     supports everything we'll ever need.
51 //
52 //   UsdAbc_AlembicConversions
53 //     The constructor of this class registers all known data conversions.
54 //     Add to the c'tor when you have a new conversion.
55 //
56 //   UsdAbc_AlembicDataReader
57 //     The backing implementation of UsdAbc_AlembicData.  It acts like a
58 //     key/value database and is itself backed by Alembic.  When an
59 //     Alembic file is opened, this scans the object/property hierarchy
60 //     and caches state for fast lookup later.  It does not do any (well,
61 //     much) value conversion until the client requests property values.
62 //
63 //     Helping this class is the _ReaderSchema, which has a table of object
64 //     types and for each type a sequence of reader functions to process
65 //     certain properties of the object and build the database mentioned
66 //     above.  The _ReaderSchemaBuilder provides a quick way to see what
67 //     objects/properties are supported and it's where to go first when
68 //     adding support for new object types.
69 //
70 //   UsdAbc_AlembicDataWriter
71 //     Unlike UsdAbc_AlembicDataReader, UsdAbc_AlembicDataWriter does not
72 //     support the SdfAbstractData API and we can't use Alembic as an
73 //     authoring layer.  That's because Alembic is not suitable for
74 //     interactive editing.  This class only supports creating/truncating
75 //     an Alembic file, dumping a layer to it and closing the file.
76 //
77 //     Helping this class is the _WriterSchema, which is similar to the
78 //     _ReaderSchema except the writer functions actually create Alembic
79 //     objects and properties instead of building a database for looking
80 //     up values later.  The _WriterSchemaBuilder provides a quick way to
81 //     see what objects/properties are supported and it's where to go first
82 //     when adding support for new object types.
83 //
84 //   UsdAbc_AlembicData
85 //     Forwards most calls to UsdAbc_AlembicDataReader.  It has a static
86 //     method for writing an Alembic file.  The UsdAbc_AlembicDataReader
87 //     exists between a successful Open() and Close().  When there is no
88 //     reader the data acts as if there's a pseudo-root prim spec at the
89 //     absolute root path.
90 //
91 
92 using namespace UsdAbc_AlembicUtil;
93 
94 TF_DEFINE_ENV_SETTING(USD_ABC_EXPAND_INSTANCES, false,
95                       "Force Alembic instances to be expanded.");
96 TF_DEFINE_ENV_SETTING(USD_ABC_DISABLE_INSTANCING, false,
97                       "Disable instancing on prototypes created from Alembic.");
98 TF_DEFINE_ENV_SETTING(USD_ABC_PARENT_INSTANCES, true,
99                       "Make parent of instance source into prototype where possible.");
100 
101 // The SdfAbstractData time samples type.
102 // XXX: SdfAbstractData should typedef this.
103 typedef std::set<double> UsdAbc_TimeSamples;
104 
105 //
106 // UsdAbc_AlembicData
107 //
108 
109 #define XXX_UNSUPPORTED(M) TF_RUNTIME_ERROR("Alembic file " #M "() not supported")
110 
UsdAbc_AlembicData(SdfFileFormat::FileFormatArguments args)111 UsdAbc_AlembicData::UsdAbc_AlembicData(SdfFileFormat::FileFormatArguments args)
112     : _arguments(std::move(args))
113 {
114 }
115 
~UsdAbc_AlembicData()116 UsdAbc_AlembicData::~UsdAbc_AlembicData()
117 {
118     // Do nothing
119 }
120 
121 UsdAbc_AlembicDataRefPtr
New(SdfFileFormat::FileFormatArguments args)122 UsdAbc_AlembicData::New(SdfFileFormat::FileFormatArguments args)
123 {
124     return TfCreateRefPtr(new UsdAbc_AlembicData(std::move(args)));
125 }
126 
127 bool
Open(const std::string & filePath)128 UsdAbc_AlembicData::Open(const std::string& filePath)
129 {
130     TfAutoMallocTag2 tag("UsdAbc_AlembicData", "UsdAbc_AlembicData::Open");
131     TRACE_FUNCTION();
132 
133     // Prepare the reader.
134     _reader.reset(new UsdAbc_AlembicDataReader);
135     // Suppress instancing support.
136     if (TfGetEnvSetting(USD_ABC_EXPAND_INSTANCES)) {
137         _reader->SetFlag(UsdAbc_AlembicContextFlagNames->expandInstances);
138     }
139     // Create instances but disallow instancing on the prototype.
140     if (TfGetEnvSetting(USD_ABC_DISABLE_INSTANCING)) {
141         _reader->SetFlag(UsdAbc_AlembicContextFlagNames->disableInstancing);
142     }
143     // Use the parent of instance sources as the Usd prototype prim, where
144     // possible.
145     if (TfGetEnvSetting(USD_ABC_PARENT_INSTANCES)) {
146         _reader->SetFlag(UsdAbc_AlembicContextFlagNames->promoteInstances);
147     }
148     //_reader->SetFlag(UsdAbc_AlembicContextFlagNames->verbose);
149 
150     // Open the archive.
151     if (_reader->Open(filePath, _arguments)) {
152         return true;
153     }
154 
155     TF_RUNTIME_ERROR("Failed to open Alembic archive \"%s\": %s",
156                      filePath.c_str(),
157                      _reader->GetErrors().c_str());
158     Close();
159     return false;
160 }
161 
162 void
Close()163 UsdAbc_AlembicData::Close()
164 {
165     _reader.reset();
166 }
167 
168 bool
Write(const SdfAbstractDataConstPtr & data,const std::string & filePath,const std::string & comment)169 UsdAbc_AlembicData::Write(
170     const SdfAbstractDataConstPtr& data,
171     const std::string& filePath,
172     const std::string& comment)
173 {
174     TfAutoMallocTag2 tag("UsdAbc_AlembicData", "UsdAbc_AlembicData::Write");
175     TRACE_FUNCTION();
176 
177     std::string finalComment = comment;
178     if (data && finalComment.empty()) {
179         VtValue value = data->Get(
180             SdfPath::AbsoluteRootPath(), SdfFieldKeys->Comment);
181         if (value.IsHolding<std::string>()) {
182             finalComment = value.UncheckedGet<std::string>();
183         }
184     }
185 
186     // Prepare the writer.
187     UsdAbc_AlembicDataWriter writer;
188     //writer.SetFlag(UsdAbc_AlembicContextFlagNames->verbose);
189 
190     // Write the archive.
191     if (writer.Open(filePath, finalComment)) {
192         if (writer.Write(data) && writer.Close()) {
193             return true;
194         }
195         TfDeleteFile(filePath);
196     }
197     TF_RUNTIME_ERROR("Alembic error: %s", writer.GetErrors().c_str());
198     return false;
199 }
200 
201 bool
StreamsData() const202 UsdAbc_AlembicData::StreamsData() const
203 {
204     return true;
205 }
206 
207 void
CreateSpec(const SdfPath & path,SdfSpecType specType)208 UsdAbc_AlembicData::CreateSpec(const SdfPath &path, SdfSpecType specType)
209 {
210     XXX_UNSUPPORTED(CreateSpec);
211 }
212 
213 bool
HasSpec(const SdfPath & path) const214 UsdAbc_AlembicData::HasSpec(const SdfPath& path) const
215 {
216     return _reader ? _reader->HasSpec(path)
217                    : (path == SdfPath::AbsoluteRootPath());
218 }
219 
220 void
EraseSpec(const SdfPath & path)221 UsdAbc_AlembicData::EraseSpec(const SdfPath& path)
222 {
223     XXX_UNSUPPORTED(EraseSpec);
224 }
225 
226 void
MoveSpec(const SdfPath & oldPath,const SdfPath & newPath)227 UsdAbc_AlembicData::MoveSpec(
228     const SdfPath& oldPath,
229     const SdfPath& newPath)
230 {
231     XXX_UNSUPPORTED(MoveSpec);
232 }
233 
234 SdfSpecType
GetSpecType(const SdfPath & path) const235 UsdAbc_AlembicData::GetSpecType(const SdfPath& path) const
236 {
237     if (_reader) {
238         return _reader->GetSpecType(path);
239     }
240     if (path == SdfPath::AbsoluteRootPath()) {
241         return SdfSpecTypePseudoRoot;
242     }
243     return SdfSpecTypeUnknown;
244 }
245 
246 void
_VisitSpecs(SdfAbstractDataSpecVisitor * visitor) const247 UsdAbc_AlembicData::_VisitSpecs(SdfAbstractDataSpecVisitor* visitor) const
248 {
249     if (_reader) {
250         _reader->VisitSpecs(*this, visitor);
251     }
252 }
253 
254 bool
Has(const SdfPath & path,const TfToken & fieldName,SdfAbstractDataValue * value) const255 UsdAbc_AlembicData::Has(
256     const SdfPath& path,
257     const TfToken& fieldName,
258     SdfAbstractDataValue* value) const
259 {
260     return _reader ? _reader->HasField(path, fieldName, value) : false;
261 }
262 
263 bool
Has(const SdfPath & path,const TfToken & fieldName,VtValue * value) const264 UsdAbc_AlembicData::Has(
265     const SdfPath& path,
266     const TfToken& fieldName,
267     VtValue* value) const
268 {
269     return _reader ? _reader->HasField(path, fieldName, value) : false;
270 }
271 
272 VtValue
Get(const SdfPath & path,const TfToken & fieldName) const273 UsdAbc_AlembicData::Get(
274     const SdfPath& path,
275     const TfToken& fieldName) const
276 {
277     VtValue result;
278     if (_reader) {
279         _reader->HasField(path, fieldName, &result);
280     }
281     return result;
282 }
283 
284 void
Set(const SdfPath & path,const TfToken & fieldName,const VtValue & value)285 UsdAbc_AlembicData::Set(
286     const SdfPath& path,
287     const TfToken& fieldName,
288     const VtValue& value)
289 {
290     XXX_UNSUPPORTED(Set);
291 }
292 
293 void
Set(const SdfPath & path,const TfToken & fieldName,const SdfAbstractDataConstValue & value)294 UsdAbc_AlembicData::Set(
295     const SdfPath& path,
296     const TfToken& fieldName,
297     const SdfAbstractDataConstValue& value)
298 {
299     XXX_UNSUPPORTED(Set);
300 }
301 
302 void
Erase(const SdfPath & path,const TfToken & fieldName)303 UsdAbc_AlembicData::Erase(
304     const SdfPath& path,
305     const TfToken& fieldName)
306 {
307     XXX_UNSUPPORTED(Erase);
308 }
309 
310 std::vector<TfToken>
List(const SdfPath & path) const311 UsdAbc_AlembicData::List(const SdfPath& path) const
312 {
313     return _reader ? _reader->List(path) : std::vector<TfToken>();
314 }
315 
316 std::set<double>
ListAllTimeSamples() const317 UsdAbc_AlembicData::ListAllTimeSamples() const
318 {
319     return _reader ? _reader->ListAllTimeSamples() : std::set<double>();
320 }
321 
322 std::set<double>
ListTimeSamplesForPath(const SdfPath & path) const323 UsdAbc_AlembicData::ListTimeSamplesForPath(const SdfPath& path) const
324 {
325     return _reader ? _reader->ListTimeSamplesForPath(path).GetTimes()
326                    : std::set<double>();
327 }
328 
329 bool
GetBracketingTimeSamples(double time,double * tLower,double * tUpper) const330 UsdAbc_AlembicData::GetBracketingTimeSamples(
331     double time, double* tLower, double* tUpper) const
332 {
333     const std::set<double>& samples = _reader->ListAllTimeSamples();
334     return UsdAbc_AlembicDataReader::TimeSamples::Bracket(samples, time,
335                                                           tLower, tUpper);
336 }
337 
338 size_t
GetNumTimeSamplesForPath(const SdfPath & path) const339 UsdAbc_AlembicData::GetNumTimeSamplesForPath(
340     const SdfPath& path) const
341 {
342     return _reader ? _reader->ListTimeSamplesForPath(path).GetSize() : 0u;
343 }
344 
345 bool
GetBracketingTimeSamplesForPath(const SdfPath & path,double time,double * tLower,double * tUpper) const346 UsdAbc_AlembicData::GetBracketingTimeSamplesForPath(
347     const SdfPath& path,
348     double time, double* tLower, double* tUpper) const
349 {
350     return _reader &&
351            _reader->ListTimeSamplesForPath(path).Bracket(time, tLower, tUpper);
352 }
353 
354 bool
QueryTimeSample(const SdfPath & path,double time,SdfAbstractDataValue * value) const355 UsdAbc_AlembicData::QueryTimeSample(
356     const SdfPath& path,
357     double time,
358     SdfAbstractDataValue* value) const
359 {
360     UsdAbc_AlembicDataReader::Index index;
361     return _reader &&
362            _reader->ListTimeSamplesForPath(path).FindIndex(time, &index) &&
363            _reader->HasValue(path, index, value);
364 }
365 
366 bool
QueryTimeSample(const SdfPath & path,double time,VtValue * value) const367 UsdAbc_AlembicData::QueryTimeSample(
368     const SdfPath& path,
369     double time,
370     VtValue* value) const
371 {
372     UsdAbc_AlembicDataReader::Index index;
373     return _reader->ListTimeSamplesForPath(path).FindIndex(time, &index) &&
374            _reader->HasValue(path, index, value);
375 }
376 
377 void
SetTimeSample(const SdfPath & path,double time,const VtValue & value)378 UsdAbc_AlembicData::SetTimeSample(
379     const SdfPath& path,
380     double time,
381     const VtValue& value)
382 {
383     XXX_UNSUPPORTED(SetTimeSample);
384 }
385 
386 void
EraseTimeSample(const SdfPath & path,double time)387 UsdAbc_AlembicData::EraseTimeSample(const SdfPath& path, double time)
388 {
389     XXX_UNSUPPORTED(EraseTimeSample);
390 }
391 
392 PXR_NAMESPACE_CLOSE_SCOPE
393 
394