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 #include "pxr/pxr.h"
25 #include "pxr/usd/plugin/usdAbc/alembicTest.h"
26 #include "pxr/usd/plugin/usdAbc/alembicData.h"
27 #include "pxr/usd/sdf/fileFormat.h"
28 #include "pxr/usd/sdf/layer.h"
29 #include "pxr/usd/sdf/schema.h"
30 #include "pxr/base/tf/ostreamMethods.h"
31 #include <algorithm>
32 #include <vector>
33 
34 PXR_NAMESPACE_OPEN_SCOPE
35 
36 
37 template <class T>
_Truncate(VtValue & v,size_t max=5)38 static bool _Truncate(VtValue& v, size_t max = 5)
39 {
40     if (v.IsHolding<VtArray<T> >()) {
41         const VtArray<T> array = v.UncheckedGet<VtArray<T> >();
42         if (array.size() > max) {
43             VtArray<T> newArray(max);
44             std::copy(array.begin(), array.begin() + max, newArray.begin());
45             v = newArray;
46             return true;
47         }
48     }
49     return false;
50 }
51 
52 // Wraps another visitor, feeding it specs in lexicographic order.
53 class UsdAbc_SortedDataSpecVisitor : public SdfAbstractDataSpecVisitor {
54 public:
UsdAbc_SortedDataSpecVisitor(SdfAbstractDataSpecVisitor * wrapped)55     explicit UsdAbc_SortedDataSpecVisitor(SdfAbstractDataSpecVisitor* wrapped)
56         : _visitor(wrapped) {}
~UsdAbc_SortedDataSpecVisitor()57     virtual ~UsdAbc_SortedDataSpecVisitor() {}
58 
59     // SdfAbstractDataSpecVisitor overrids
VisitSpec(const SdfAbstractData & data,const SdfPath & path)60     virtual bool VisitSpec(const SdfAbstractData& data,
61                            const SdfPath& path) {
62         if (_visitor)
63             _paths.push_back(path);
64         return true;
65     }
66 
Done(const SdfAbstractData & data)67     virtual void Done(const SdfAbstractData& data) {
68         if (_visitor) {
69             // Sort ids.
70             std::sort(_paths.begin(), _paths.end());
71 
72             // Pass ids to the wrapped visitor.
73             for (const auto& path : _paths) {
74                 if (_Pass(data, path)) {
75                     if (!_visitor->VisitSpec(data, path)) {
76                         break;
77                     }
78                 }
79             }
80 
81             // Finish up.
82             _visitor->Done(data);
83             _paths.clear();
84         }
85     }
86 
87 protected:
88     /// Iff this returns \c true, \p id is passed to the wrapped visitor.
89     /// The default returns \c true.
_Pass(const SdfAbstractData & data,const SdfPath & id)90     virtual bool _Pass(const SdfAbstractData& data,
91                        const SdfPath& id) {
92         return true;
93     }
94 
95 private:
96     SdfAbstractDataSpecVisitor* _visitor;
97 
98     typedef std::vector<SdfPath> _Paths;
99     _Paths _paths;
100 };
101 
102 
103 
104 // Note that this works because the Alembic data visits in hierarchy order.
105 struct UsdAbc_AlembicWriteVisitor : public SdfAbstractDataSpecVisitor {
VisitSpecUsdAbc_AlembicWriteVisitor106     virtual bool VisitSpec(const SdfAbstractData& data,
107                            const SdfPath& path)
108     {
109         if (path == SdfPath::AbsoluteRootPath()) {
110             // Ignore.
111         }
112         else {
113             fprintf(stdout, "%*s", 2*int(path.GetPathElementCount()-1), "");
114             if (path.IsPropertyPath()) {
115                 VtValue custom = data.Get(path, SdfFieldKeys->Custom);
116                 if (custom.IsHolding<bool>()) {
117                     fprintf(stdout, "%s",
118                             custom.UncheckedGet<bool>() ? "custom " : "");
119                 }
120                 else if (!custom.IsEmpty()) {
121                     fprintf(stdout, "!BAD_CUSTOM ");
122                 }
123 
124                 VtValue typeName = data.Get(path, SdfFieldKeys->TypeName);
125                 if (typeName.IsHolding<TfToken>()) {
126                     fprintf(stdout, "%s ", TfStringify(typeName).c_str());
127                 }
128                 else if (!typeName.IsEmpty()) {
129                     fprintf(stdout, "!BAD_TYPE ");
130                 }
131 
132                 fprintf(stdout, "%s", path.GetName().c_str());
133 
134                 VtValue value = data.Get(path, SdfFieldKeys->Default);
135                 if (!value.IsEmpty()) {
136                     // Truncate shaped types to not dump too much data.
137                     const char* trailing = NULL;
138                     if (value.IsArrayValued()) {
139                         if (_Truncate<bool>(value) ||
140                             _Truncate<double>(value) ||
141                             _Truncate<float>(value) ||
142                             _Truncate<GfMatrix2d>(value) ||
143                             _Truncate<GfMatrix3d>(value) ||
144                             _Truncate<GfMatrix4d>(value) ||
145                             _Truncate<GfVec2d>(value) ||
146                             _Truncate<GfVec2f>(value) ||
147                             _Truncate<GfVec2i>(value) ||
148                             _Truncate<GfVec3d>(value) ||
149                             _Truncate<GfVec3f>(value) ||
150                             _Truncate<GfVec3i>(value) ||
151                             _Truncate<GfVec4d>(value) ||
152                             _Truncate<GfVec4f>(value) ||
153                             _Truncate<GfVec4i>(value) ||
154                             _Truncate<int>(value) ||
155                             _Truncate<SdfAssetPath>(value) ||
156                             _Truncate<std::string>(value) ||
157                             _Truncate<TfToken>(value)) {
158                             trailing = "...";
159                         }
160                     }
161                     std::string s = TfStringify(value);
162                     if (trailing) {
163                         s.insert(s.size() - 1, trailing);
164                     }
165                     if (value.IsHolding<std::string>()) {
166                         s = '"' + s + '"';
167                     }
168                     fprintf(stdout, " = %s\n", s.c_str());
169                 }
170 
171                 VtValue samples = data.Get(path, SdfFieldKeys->TimeSamples);
172                 std::set<double> times = data.ListTimeSamplesForPath(path);
173                 if (samples.IsEmpty()) {
174                     if (times.size() <= 1) {
175                         // Expected.
176                     }
177                     else {
178                         fprintf(stdout, "%*s",
179                                 2*int(path.GetPathElementCount()-1), "");
180                         fprintf(stdout, "!NO_SAMPLES, want %zd\n",
181                                 times.size());
182                     }
183                 }
184                 else if (samples.IsHolding<SdfTimeSampleMap>()) {
185                     const SdfTimeSampleMap& samplesMap =
186                         samples.UncheckedGet<SdfTimeSampleMap>();
187                     if (times.size() != samplesMap.size()) {
188                         fprintf(stdout, "%*s",
189                                 2*int(path.GetPathElementCount()-1), "");
190                         fprintf(stdout, "!SAMPLES_MISMATCH, "
191                                 "have %zd vs want %zd\n",
192                                 samplesMap.size(), times.size());
193                     }
194                     else {
195                         // XXX: Should compare times in samplesMap and
196                         //      times.
197                         fprintf(stdout, "%*s",
198                                 2*int(path.GetPathElementCount()-1), "");
199                         fprintf(stdout, "samples_at=[ ");
200                         for (double t : times) {
201                             fprintf(stdout, "%g ", t);
202                         }
203                         fprintf(stdout, "]\n");
204                     }
205                 }
206                 else {
207                     fprintf(stdout, "%*s",
208                             2*int(path.GetPathElementCount()-1), "");
209                     fprintf(stdout, "!BAD_SAMPLES\n");
210                 }
211 
212                 // Write other fields.
213                 TfTokenVector tmp = data.List(path);
214                 std::set<TfToken> tokens(tmp.begin(), tmp.end());
215                 tokens.erase(SdfFieldKeys->Custom);
216                 tokens.erase(SdfFieldKeys->TypeName);
217                 tokens.erase(SdfFieldKeys->Default);
218                 tokens.erase(SdfFieldKeys->TimeSamples);
219                 const SdfSchema& schema = SdfSchema::GetInstance();
220                 for (const auto& field : tokens) {
221                     const VtValue value = data.Get(path, field);
222                     if (value != schema.GetFallback(field)) {
223                         fprintf(stdout, "%*s# %s = %s\n",
224                                 2*int(path.GetPathElementCount()-1), "",
225                                 field.GetText(),
226                                 TfStringify(value).c_str());
227                     }
228                 }
229             }
230             else {
231                 VtValue specifier = data.Get(path, SdfFieldKeys->Specifier);
232                 if (specifier.IsHolding<SdfSpecifier>()) {
233                     static const char* spec[] = { "def", "over", "class" };
234                     fprintf(stdout, "%s ",
235                             spec[specifier.UncheckedGet<SdfSpecifier>()]);
236                 }
237                 else {
238                     fprintf(stdout, "!BAD_SPEC ");
239                 }
240 
241                 VtValue typeName = data.Get(path, SdfFieldKeys->TypeName);
242                 if (typeName.IsHolding<TfToken>()) {
243                     fprintf(stdout, "%s ", TfStringify(typeName).c_str());
244                 }
245                 else if (!typeName.IsEmpty()) {
246                     fprintf(stdout, "!BAD_TYPE ");
247                 }
248 
249                 fprintf(stdout, "%s\n", path.GetName().c_str());
250             }
251         }
252         return true;
253     }
254 
DoneUsdAbc_AlembicWriteVisitor255     virtual void Done(const SdfAbstractData&)
256     {
257         // Do nothing
258     }
259 };
260 
UsdAbc_PrintTimes(const char * msg,const std::set<double> & times)261 static void UsdAbc_PrintTimes(const char* msg, const std::set<double>& times)
262 {
263     fprintf(stdout, "%s: [", msg);
264     for (double t : times) {
265         fprintf(stdout, " %f", t);
266     }
267     fprintf(stdout, " ]\n");
268 }
269 
270 struct UsdAbc_AlembicTimeVisitor : public SdfAbstractDataSpecVisitor {
VisitSpecUsdAbc_AlembicTimeVisitor271     virtual bool VisitSpec(const SdfAbstractData& data,
272                            const SdfPath& path)
273     {
274         if (path.IsPropertyPath()) {
275             UsdAbc_PrintTimes(path.GetText(),
276                               data.ListTimeSamplesForPath(path));
277         }
278         return true;
279     }
280 
DoneUsdAbc_AlembicTimeVisitor281     virtual void Done(const SdfAbstractData&)
282     {
283         // Do nothing
284     }
285 };
286 
287 bool
UsdAbc_TestAlembic(const std::string & pathname)288 UsdAbc_TestAlembic(const std::string& pathname)
289 {
290     if (UsdAbc_AlembicDataRefPtr data = UsdAbc_AlembicData::New()) {
291         if (data->Open(pathname)) {
292             // Dump prims and properties.
293             fprintf(stdout, "\nWrite:\n");
294             UsdAbc_AlembicWriteVisitor writeVisitor;
295             UsdAbc_SortedDataSpecVisitor sortWrite(&writeVisitor);
296             data->VisitSpecs(&sortWrite);
297 
298 /*
299             // Time samples.
300             fprintf(stdout, "\nTime samples:\n");
301             UsdAbc_AlembicTimeVisitor timeVisitor;
302             UsdAbc_SortedDataSpecVisitor sortTime(&timeVisitor);
303             data->VisitSpecs(&sortTime);
304             UsdAbc_PrintTimes("all", data->ListAllTimeSamples());
305 */
306 
307             // Dump all time samples of a particular property.  This is
308             // intended for the standard Alembic octopus file.
309             SdfPath path("/octopus_low/octopus_lowShape.extent");
310             std::set<double> times = data->ListTimeSamplesForPath(path);
311             if (!times.empty()) {
312                 fprintf(stdout, "\nExtent samples:\n");
313                 for (double t : times) {
314                     VtValue value;
315                     if (data->QueryTimeSample(path, t, &value)) {
316                         fprintf(stdout, "  %f: %s\n",
317                                 t, TfStringify(value).c_str());
318                     }
319                     else {
320                         fprintf(stdout, "  %f: <no value>\n", t);
321                     }
322                 }
323 
324                 // Verify no samples at times not listed.
325                 if (times.size() > 1) {
326                     double t      = floor(*times.begin());
327                     double tUpper = ceil(*times.rbegin());
328                     for (; t <= tUpper; t += 1.0) {
329                         if (times.find(t) == times.end()) {
330                             if (data->QueryTimeSample(path, t, (VtValue*)NULL)) {
331                                 fprintf(stdout, "  %f: <expected sample>\n", t);
332                             }
333                         }
334                     }
335                 }
336             }
337 
338             return true;
339         }
340         else {
341             fprintf(stderr, "Can't open Alembic file \"%s\"\n", pathname.c_str());
342         }
343     }
344     else {
345         fprintf(stderr, "Can't create Alembic data\n");
346     }
347     return false;
348 }
349 
350 bool
UsdAbc_WriteAlembic(const std::string & srcPathname,const std::string & dstPathname)351 UsdAbc_WriteAlembic(const std::string& srcPathname, const std::string& dstPathname)
352 {
353     SdfLayerRefPtr layer = SdfLayer::OpenAsAnonymous(srcPathname);
354     if (!layer) {
355         fprintf(stderr, "Can't open '%s'\n", srcPathname.c_str());
356         return false;
357     }
358 
359     // Write the file back out in the cwd.
360     return
361         SdfFileFormat::FindByExtension(".abc")->
362             WriteToFile(*get_pointer(layer), dstPathname);
363 }
364 
365 PXR_NAMESPACE_CLOSE_SCOPE
366 
367