/src/alembic/lib/Alembic/Abc/IObject.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //-***************************************************************************** |
2 | | // |
3 | | // Copyright (c) 2009-2012, |
4 | | // Sony Pictures Imageworks, Inc. and |
5 | | // Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd. |
6 | | // |
7 | | // All rights reserved. |
8 | | // |
9 | | // Redistribution and use in source and binary forms, with or without |
10 | | // modification, are permitted provided that the following conditions are |
11 | | // met: |
12 | | // * Redistributions of source code must retain the above copyright |
13 | | // notice, this list of conditions and the following disclaimer. |
14 | | // * Redistributions in binary form must reproduce the above |
15 | | // copyright notice, this list of conditions and the following disclaimer |
16 | | // in the documentation and/or other materials provided with the |
17 | | // distribution. |
18 | | // * Neither the name of Sony Pictures Imageworks, nor |
19 | | // Industrial Light & Magic nor the names of their contributors may be used |
20 | | // to endorse or promote products derived from this software without specific |
21 | | // prior written permission. |
22 | | // |
23 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
26 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
27 | | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
28 | | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
29 | | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
30 | | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
31 | | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
32 | | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
33 | | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | | // |
35 | | //-***************************************************************************** |
36 | | |
37 | | #include <Alembic/Abc/IObject.h> |
38 | | #include <Alembic/Abc/IArchive.h> |
39 | | #include <Alembic/Abc/ICompoundProperty.h> |
40 | | #include <Alembic/Abc/ITypedScalarProperty.h> |
41 | | |
42 | | namespace Alembic { |
43 | | namespace Abc { |
44 | | namespace ALEMBIC_VERSION_NS { |
45 | | |
46 | | //-***************************************************************************** |
47 | | // Nothing at the moment, this is just here as a debug entry point for |
48 | | // tracking down problems with reference counting. |
49 | | IObject::~IObject() |
50 | 33 | { |
51 | | // Nothing for now. |
52 | | // Mostly here in case we need to add reference-counting debug code. |
53 | | //std::cout << "IObject::~IObject() name: " |
54 | | // << m_object->getName() |
55 | | // << std::endl |
56 | | // << "\tUse count of writer ptr: " |
57 | | // << m_object.use_count() << std::endl; |
58 | 33 | } |
59 | | |
60 | | namespace { |
61 | | |
62 | | const AbcA::ObjectHeader g_ohd; |
63 | | |
64 | | } |
65 | | |
66 | | //-***************************************************************************** |
67 | | const AbcA::ObjectHeader &IObject::getHeader() const |
68 | 33 | { |
69 | 66 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getHeader()" ); |
70 | | |
71 | 66 | if ( m_object ) |
72 | 33 | { |
73 | 33 | return m_object->getHeader(); |
74 | 33 | } |
75 | | |
76 | 66 | ALEMBIC_ABC_SAFE_CALL_END(); |
77 | | |
78 | | // Not all error handlers throw, so have a default behavior. |
79 | 0 | return g_ohd; |
80 | 33 | }; |
81 | | |
82 | | //-***************************************************************************** |
83 | | const std::string &IObject::getName() const |
84 | 0 | { |
85 | | // Get the name of the original object |
86 | 0 | if ( m_instanceObject ) |
87 | 0 | { |
88 | 0 | return m_instanceObject->getHeader().getName(); |
89 | 0 | } |
90 | | |
91 | 0 | return m_object->getHeader().getName(); |
92 | 0 | } |
93 | | |
94 | | //-***************************************************************************** |
95 | | const std::string &IObject::getFullName() const |
96 | 0 | { |
97 | 0 | if ( !m_instancedFullName.empty() ) |
98 | 0 | { |
99 | 0 | return m_instancedFullName; |
100 | 0 | } |
101 | | |
102 | 0 | return getHeader().getFullName(); |
103 | 0 | } |
104 | | |
105 | | //-***************************************************************************** |
106 | | IArchive IObject::getArchive() const |
107 | 0 | { |
108 | |
|
109 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getArchive()" ); |
110 | | |
111 | | // proxies and targets are currently required to be in the |
112 | | // same archive. Just use the m_object archive. |
113 | 0 | if ( m_object ) |
114 | 0 | { |
115 | 0 | return IArchive( m_object->getArchive(), |
116 | 0 | getErrorHandlerPolicy() ); |
117 | 0 | } |
118 | |
|
119 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
120 | | |
121 | | // Not all error handlers throw. Have a default. |
122 | 0 | return IArchive(); |
123 | 0 | } |
124 | | |
125 | | namespace { // anonymous |
126 | | |
127 | | static inline |
128 | | std::string readInstanceSource( AbcA::CompoundPropertyReaderPtr iProp ) |
129 | 0 | { |
130 | 0 | if ( !iProp || !iProp->getPropertyHeader(".instanceSource") ) |
131 | 0 | { |
132 | 0 | return std::string(); |
133 | 0 | } |
134 | | |
135 | 0 | IStringProperty instanceSourceProp( iProp, ".instanceSource" ); |
136 | 0 | if ( !instanceSourceProp ) |
137 | 0 | return std::string(); |
138 | | |
139 | 0 | return instanceSourceProp.getValue(); |
140 | 0 | } |
141 | | |
142 | | static inline |
143 | | AbcA::ObjectReaderPtr objectReaderByName( AbcA::ObjectReaderPtr iObj, |
144 | | const std::string & iInstanceSource ); |
145 | | |
146 | | static inline |
147 | | AbcA::ObjectReaderPtr recurse( AbcA::ObjectReaderPtr iObj, |
148 | | const std::string & iInstanceSource, |
149 | | std::size_t iCurPos ) |
150 | 0 | { |
151 | 0 | std::size_t nextSlash = iInstanceSource.find( '/', iCurPos ); |
152 | 0 | std::string childName; |
153 | 0 | if ( nextSlash == std::string::npos ) |
154 | 0 | { |
155 | 0 | childName = iInstanceSource.substr( iCurPos ); |
156 | 0 | } |
157 | 0 | else |
158 | 0 | { |
159 | 0 | childName = iInstanceSource.substr( iCurPos, nextSlash - iCurPos ); |
160 | 0 | } |
161 | |
|
162 | 0 | AbcA::ObjectReaderPtr child = iObj->getChild( childName ); |
163 | |
|
164 | 0 | if ( child && nextSlash != std::string::npos ) |
165 | 0 | { |
166 | | // we hit an instance so we have to evaluate down to the correct spot |
167 | 0 | if ( child->getMetaData().get("isInstance") == "1" ) |
168 | 0 | { |
169 | | // get and recursively walk down this other path |
170 | 0 | AbcA::CompoundPropertyReaderPtr prop = child->getProperties(); |
171 | 0 | std::string instanceSource = readInstanceSource( prop ); |
172 | 0 | child = objectReaderByName( child, instanceSource); |
173 | 0 | } |
174 | 0 | return recurse( child, iInstanceSource, nextSlash + 1 ); |
175 | 0 | } |
176 | | |
177 | | // child not found, or we are on our last child |
178 | 0 | return child; |
179 | 0 | } |
180 | | |
181 | | //-***************************************************************************** |
182 | | static inline |
183 | | AbcA::ObjectReaderPtr objectReaderByName( AbcA::ObjectReaderPtr iObj, |
184 | | const std::string & iInstanceSource ) |
185 | 0 | { |
186 | 0 | if ( iInstanceSource.empty() || ! iObj ) |
187 | 0 | return AbcA::ObjectReaderPtr(); |
188 | | |
189 | 0 | std::size_t curPos = 0; |
190 | 0 | if ( iInstanceSource[0] == '/' ) |
191 | 0 | { |
192 | 0 | curPos = 1; |
193 | 0 | } |
194 | |
|
195 | 0 | AbcA::ObjectReaderPtr obj = iObj->getArchive()->getTop(); |
196 | 0 | return recurse( obj, iInstanceSource, curPos ); |
197 | 0 | } |
198 | | |
199 | | //-***************************************************************************** |
200 | | static inline |
201 | | std::string getParentFullName( const std::string& iChildFullName ) |
202 | 0 | { |
203 | 0 | size_t pos = iChildFullName.rfind('/'); |
204 | |
|
205 | 0 | if ( pos == std::string::npos || pos == 0 ) |
206 | 0 | { |
207 | 0 | return std::string(); |
208 | 0 | } |
209 | | |
210 | 0 | return iChildFullName.substr(0, pos); |
211 | 0 | } |
212 | | |
213 | | } // end anonymous namespace |
214 | | |
215 | | //-***************************************************************************** |
216 | | IObject IObject::getParent() const |
217 | 0 | { |
218 | |
|
219 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getParent()" ); |
220 | |
|
221 | 0 | if ( !m_instancedFullName.empty() ) |
222 | 0 | { |
223 | 0 | std::string parentFullName = getParentFullName( m_instancedFullName ); |
224 | |
|
225 | 0 | AbcA::ObjectReaderPtr parentPtr = m_object->getParent(); |
226 | 0 | bool setFullName = false; |
227 | | |
228 | | // if the instanced full name doesn't match the parents full name |
229 | | // then we have an instanced situation where we need to carefully |
230 | | // walk the hierarchy to make sure we end up with the correct parent |
231 | | |
232 | | // If the names do match, then the parent isn't a part of the instance |
233 | | // and so we don't need to set that full name |
234 | 0 | if ( parentPtr && !parentFullName.empty() && |
235 | 0 | parentFullName != parentPtr->getFullName() ) |
236 | 0 | { |
237 | 0 | parentPtr = objectReaderByName( parentPtr, parentFullName ); |
238 | 0 | setFullName = true; |
239 | 0 | } |
240 | |
|
241 | 0 | IObject obj( parentPtr, |
242 | 0 | getErrorHandlerPolicy() ); |
243 | |
|
244 | 0 | if ( setFullName ) |
245 | 0 | { |
246 | 0 | obj.setInstancedFullName( parentFullName ); |
247 | 0 | } |
248 | |
|
249 | 0 | return obj; |
250 | 0 | } |
251 | 0 | else if ( m_object ) |
252 | 0 | { |
253 | 0 | return IObject( m_object->getParent(), |
254 | 0 | getErrorHandlerPolicy() ); |
255 | 0 | } |
256 | |
|
257 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
258 | | |
259 | | // Not all error handlers throw. Have a default. |
260 | 0 | return IObject(); |
261 | 0 | } |
262 | | |
263 | | //-***************************************************************************** |
264 | | size_t IObject::getNumChildren() const |
265 | 33 | { |
266 | | |
267 | 66 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getNumChildren()" ); |
268 | | |
269 | 66 | if ( m_object ) |
270 | 33 | { |
271 | 33 | return m_object->getNumChildren(); |
272 | 33 | } |
273 | | |
274 | 66 | ALEMBIC_ABC_SAFE_CALL_END(); |
275 | | |
276 | | // Not all error handlers throw, have a default. |
277 | 0 | return 0; |
278 | 33 | } |
279 | | |
280 | | //-***************************************************************************** |
281 | | const AbcA::ObjectHeader &IObject::getChildHeader( size_t iIdx ) const |
282 | 0 | { |
283 | |
|
284 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getChildHeader()" ); |
285 | |
|
286 | 0 | if ( m_object ) |
287 | 0 | { |
288 | 0 | return m_object->getChildHeader( iIdx ); |
289 | 0 | } |
290 | |
|
291 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
292 | | |
293 | | // Not all error handlers throw, have a default. |
294 | 0 | static const AbcA::ObjectHeader hd; |
295 | 0 | return hd; |
296 | 0 | } |
297 | | |
298 | | //-***************************************************************************** |
299 | | const AbcA::ObjectHeader * |
300 | | IObject::getChildHeader( const std::string &iName ) const |
301 | 0 | { |
302 | |
|
303 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getChildHeader( name )" ); |
304 | |
|
305 | 0 | if ( m_object ) |
306 | 0 | { |
307 | 0 | return m_object->getChildHeader( iName ); |
308 | 0 | } |
309 | |
|
310 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
311 | | |
312 | | // Not all error handlers throw, have a default. |
313 | 0 | return NULL; |
314 | 0 | } |
315 | | |
316 | | //-***************************************************************************** |
317 | | IObject IObject::getChild( size_t iChildIndex ) const |
318 | 0 | { |
319 | |
|
320 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getChild()" ); |
321 | |
|
322 | 0 | if ( m_object ) |
323 | 0 | { |
324 | 0 | IObject obj( m_object->getChild( iChildIndex ), |
325 | 0 | getErrorHandlerPolicy() ); |
326 | |
|
327 | 0 | if ( !m_instancedFullName.empty() ) |
328 | 0 | { |
329 | 0 | obj.setInstancedFullName( |
330 | 0 | m_instancedFullName + std::string("/") + obj.getName() ); |
331 | 0 | } |
332 | |
|
333 | 0 | return obj; |
334 | 0 | } |
335 | |
|
336 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
337 | | |
338 | | // Not all error handlers throw, return something in case. |
339 | 0 | return IObject(); |
340 | 0 | } |
341 | | |
342 | | //-***************************************************************************** |
343 | | IObject IObject::getChild( const std::string &iChildName ) const |
344 | 0 | { |
345 | |
|
346 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getChild()" ); |
347 | |
|
348 | 0 | if ( m_object ) |
349 | 0 | { |
350 | 0 | IObject obj( m_object->getChild( iChildName ), |
351 | 0 | getErrorHandlerPolicy() ); |
352 | |
|
353 | 0 | if ( !m_instancedFullName.empty() ) |
354 | 0 | { |
355 | 0 | obj.setInstancedFullName( |
356 | 0 | m_instancedFullName + std::string("/") + obj.getName() ); |
357 | 0 | } |
358 | |
|
359 | 0 | return obj; |
360 | 0 | } |
361 | |
|
362 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
363 | | |
364 | | // Not all error handlers throw, return something in case. |
365 | 0 | return IObject(); |
366 | 0 | } |
367 | | |
368 | | //-***************************************************************************** |
369 | | void IObject::reset() |
370 | 0 | { |
371 | 0 | m_instanceObject.reset(); |
372 | 0 | m_instancedFullName.clear(); |
373 | |
|
374 | 0 | m_object.reset(); |
375 | 0 | Base::reset(); |
376 | 0 | } |
377 | | |
378 | | //-***************************************************************************** |
379 | | ICompoundProperty IObject::getProperties() const |
380 | 0 | { |
381 | |
|
382 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getProperties()" ); |
383 | |
|
384 | 0 | if ( m_object ) |
385 | 0 | { |
386 | 0 | return ICompoundProperty( m_object->getProperties() ); |
387 | 0 | } |
388 | |
|
389 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
390 | | |
391 | | // Not all error handlers throw, have a default. |
392 | 0 | return ICompoundProperty(); |
393 | 0 | } |
394 | | |
395 | | //-***************************************************************************** |
396 | | bool IObject::getPropertiesHash( Util::Digest & oDigest ) |
397 | 0 | { |
398 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getPropertiesHash()" ); |
399 | |
|
400 | 0 | if ( m_object ) |
401 | 0 | { |
402 | 0 | return m_object->getPropertiesHash( oDigest ); |
403 | 0 | } |
404 | |
|
405 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
406 | | |
407 | | // Not all error handlers throw, have a default. |
408 | 0 | return false; |
409 | 0 | } |
410 | | |
411 | | |
412 | | //-***************************************************************************** |
413 | | bool IObject::getChildrenHash( Util::Digest & oDigest ) |
414 | 0 | { |
415 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::getChildrenHash()" ); |
416 | |
|
417 | 0 | if ( m_object ) |
418 | 0 | { |
419 | 0 | return m_object->getChildrenHash( oDigest ); |
420 | 0 | } |
421 | |
|
422 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
423 | | |
424 | | // Not all error handlers throw, have a default. |
425 | 0 | return false; |
426 | 0 | } |
427 | | |
428 | | //-***************************************************************************** |
429 | | bool IObject::isInstanceRoot() const |
430 | 0 | { |
431 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::isInstanceRoot()" ); |
432 | |
|
433 | 0 | if ( m_instanceObject ) |
434 | 0 | { |
435 | 0 | return true; |
436 | 0 | } |
437 | |
|
438 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
439 | | |
440 | 0 | return false; |
441 | 0 | } |
442 | | |
443 | | //-***************************************************************************** |
444 | | bool IObject::isInstanceDescendant() const |
445 | 0 | { |
446 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::isInstanceDescendant()" ); |
447 | |
|
448 | 0 | if ( !m_instancedFullName.empty() ) |
449 | 0 | { |
450 | 0 | return true; |
451 | 0 | } |
452 | |
|
453 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
454 | | |
455 | 0 | return false; |
456 | 0 | } |
457 | | |
458 | | //-***************************************************************************** |
459 | | std::string IObject::instanceSourcePath() const |
460 | 0 | { |
461 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::instanceSourcePath()" ); |
462 | |
|
463 | 0 | if ( !m_instanceObject ) |
464 | 0 | { |
465 | 0 | return std::string(); |
466 | 0 | } |
467 | | |
468 | 0 | AbcA::CompoundPropertyReaderPtr props = m_instanceObject->getProperties(); |
469 | 0 | return readInstanceSource( props ); |
470 | |
|
471 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
472 | | |
473 | 0 | return std::string(); |
474 | 0 | } |
475 | | |
476 | | //-***************************************************************************** |
477 | | void IObject::setInstancedFullName( const std::string& parentPath ) const |
478 | 0 | { |
479 | 0 | m_instancedFullName = parentPath; |
480 | 0 | } |
481 | | |
482 | | //-***************************************************************************** |
483 | | bool IObject::isChildInstance( size_t iChildIndex ) const |
484 | 0 | { |
485 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( |
486 | 0 | "IObject::isChildInstanced(size_t iChildIndex)" ); |
487 | |
|
488 | 0 | IObject child = getChild( iChildIndex ); |
489 | |
|
490 | 0 | if ( child.valid() ) |
491 | 0 | { |
492 | 0 | return child.isInstanceRoot(); |
493 | 0 | } |
494 | |
|
495 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
496 | | |
497 | 0 | return false; |
498 | 0 | } |
499 | | |
500 | | //-***************************************************************************** |
501 | | bool IObject::isChildInstance( const std::string &iChildName ) const |
502 | 0 | { |
503 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( |
504 | 0 | "IObject::isChildInstance(const std::string &iChildName)" ); |
505 | |
|
506 | 0 | IObject child = getChild( iChildName ); |
507 | |
|
508 | 0 | if ( child.valid() ) |
509 | 0 | { |
510 | 0 | return child.isInstanceRoot(); |
511 | 0 | } |
512 | |
|
513 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
514 | | |
515 | 0 | return false; |
516 | 0 | } |
517 | | |
518 | | //-***************************************************************************** |
519 | | void IObject::init( IArchive & iArchive, const Argument &iArg0 ) |
520 | 0 | { |
521 | | // Set the error handling policy |
522 | 0 | getErrorHandler().setPolicy( |
523 | 0 | GetErrorHandlerPolicy( iArchive, iArg0 ) ); |
524 | |
|
525 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::init( IArchive )" ); |
526 | |
|
527 | 0 | m_object = iArchive.getTop().getPtr(); |
528 | |
|
529 | 0 | ALEMBIC_ABC_SAFE_CALL_END_RESET(); |
530 | 0 | } |
531 | | |
532 | | //-***************************************************************************** |
533 | | void IObject::init( AbcA::ObjectReaderPtr iParent, |
534 | | const std::string &iName, |
535 | | ErrorHandler::Policy iPolicy ) |
536 | 0 | { |
537 | 0 | ALEMBIC_ABC_SAFE_CALL_BEGIN( "IObject::init()" ); |
538 | |
|
539 | 0 | getErrorHandler().setPolicy( iPolicy ); |
540 | |
|
541 | 0 | m_object = iParent->getChild( iName ); |
542 | |
|
543 | 0 | ALEMBIC_ABC_SAFE_CALL_END(); |
544 | 0 | } |
545 | | |
546 | | //-***************************************************************************** |
547 | | void IObject::initInstance() |
548 | 33 | { |
549 | | |
550 | | // not an instance so m_instanceObject will stay empty |
551 | 33 | if ( !m_object || m_object->getMetaData().get("isInstance") != "1") |
552 | 33 | { |
553 | 33 | return; |
554 | 33 | } |
555 | | |
556 | 0 | AbcA::CompoundPropertyReaderPtr propsPtr = m_object->getProperties(); |
557 | 0 | std::string instanceSource = readInstanceSource( propsPtr ); |
558 | 0 | AbcA::ObjectReaderPtr targetObject = |
559 | 0 | objectReaderByName( m_object, instanceSource ); |
560 | |
|
561 | 0 | m_instanceObject = m_object; |
562 | 0 | m_object = targetObject; |
563 | | |
564 | | // initialize the full name to the instance full name |
565 | 0 | if ( m_instanceObject != 0 ) |
566 | 0 | { |
567 | 0 | m_instancedFullName = m_instanceObject->getFullName(); |
568 | 0 | } |
569 | 0 | } |
570 | | |
571 | | } // End namespace ALEMBIC_VERSION_NS |
572 | | } // End namespace Abc |
573 | | } // End namespace Alembic |