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