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