1 //-*****************************************************************************
2 //
3 // Copyright (c) 2009-2014,
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 "AbcWriteJob.h"
38 
39 #ifdef ALEMBIC_WITH_HDF5
40 #include <Alembic/AbcCoreHDF5/All.h>
41 #endif
42 
43 #include <Alembic/AbcCoreOgawa/All.h>
44 namespace
45 {
hasDuplicates(const util::ShapeSet & dagPath,unsigned int stripDepth)46     void hasDuplicates(const util::ShapeSet & dagPath, unsigned int stripDepth)
47     {
48         std::map<std::string, MDagPath> roots;
49         const util::ShapeSet::const_iterator end = dagPath.end();
50         for (util::ShapeSet::const_iterator it = dagPath.begin();
51             it != end; it++)
52         {
53             std::string fullName = it->fullPathName().asChar();
54             if (!fullName.empty() && fullName[0] == '|')
55             {
56                 fullName = fullName.substr(1);
57             }
58 
59             if (stripDepth > 0)
60             {
61                 MString name = fullName.c_str();
62                 fullName = util::stripNamespaces(name, stripDepth).asChar();
63             }
64 
65             std::map<std::string, MDagPath>::iterator strIt =
66                 roots.find(fullName);
67             if (strIt != roots.end())
68             {
69                 std::string theError = "Conflicting root node names specified: ";
70                 theError += it->fullPathName().asChar();
71                 theError += " ";
72                 theError += strIt->second.fullPathName().asChar();
73                 if (stripDepth > 0)
74                 {
75                     theError += " with -stripNamespace specified.";
76                 }
77                 throw std::runtime_error(theError);
78             }
79             else
80             {
81                 roots[fullName] = *it;
82             }
83         }
84     }
85 
addToString(std::string & str,const std::string & name,unsigned int value)86     void addToString(std::string & str,
87         const std::string & name, unsigned int value)
88     {
89         if (value > 0)
90         {
91             std::stringstream ss;
92             ss << value;
93             str += name + std::string(" ") + ss.str() + std::string(" ");
94         }
95     }
96 
processCallback(std::string iCallback,bool isMelCallback,double iFrame,const MBoundingBox & iBbox)97     void processCallback(std::string iCallback, bool isMelCallback,
98         double iFrame, const MBoundingBox & iBbox)
99     {
100         if (iCallback.empty())
101             return;
102 
103         size_t pos = iCallback.find("#FRAME#");
104         if ( pos != std::string::npos )
105         {
106             std::stringstream sstrm;
107             sstrm.precision(std::numeric_limits<double>::digits10);
108             sstrm << iFrame;
109             std::string str = sstrm.str();
110 
111             iCallback.replace(pos, 7, str);
112         }
113 
114         pos = iCallback.find("#BOUNDS#");
115         if ( pos != std::string::npos )
116         {
117             std::stringstream sstrm;
118             sstrm.precision(std::numeric_limits<float>::digits10);
119             sstrm << " " << iBbox.min().x << " " << iBbox.min().y << " " <<
120                 iBbox.min().z << " " << iBbox.max().x << " " <<
121                 iBbox.max().y << " " <<iBbox.max().z;
122 
123             std::string str = sstrm.str();
124 
125             iCallback.replace(pos, 8, str);
126         }
127 
128         pos = iCallback.find("#BOUNDSARRAY#");
129         if ( pos != std::string::npos )
130         {
131             std::stringstream sstrm;
132             sstrm.precision(std::numeric_limits<float>::digits10);
133 
134             if (isMelCallback)
135             {
136                 sstrm << " {";
137             }
138             else
139             {
140                 sstrm << " [";
141             }
142 
143             sstrm << iBbox.min().x << "," << iBbox.min().y << "," <<
144                 iBbox.min().z << "," << iBbox.max().x << "," <<
145                 iBbox.max().y << "," << iBbox.max().z;
146 
147             if (isMelCallback)
148             {
149                 sstrm << "} ";
150             }
151             else
152             {
153                 sstrm << "] ";
154             }
155             std::string str = sstrm.str();
156             iCallback.replace(pos, 13, str);
157         }
158 
159         if (isMelCallback)
160             MGlobal::executeCommand(iCallback.c_str(), true);
161         else
162             MGlobal::executePythonCommand(iCallback.c_str(), true);
163     }
164 }
165 
AbcWriteJob(const char * iFileName,bool iAsOgawa,std::set<double> & iTransFrames,Alembic::AbcCoreAbstract::TimeSamplingPtr iTransTime,std::set<double> & iShapeFrames,Alembic::AbcCoreAbstract::TimeSamplingPtr iShapeTime,const JobArgs & iArgs)166 AbcWriteJob::AbcWriteJob(const char * iFileName,
167     bool iAsOgawa,
168     std::set<double> & iTransFrames,
169     Alembic::AbcCoreAbstract::TimeSamplingPtr iTransTime,
170     std::set<double> & iShapeFrames,
171     Alembic::AbcCoreAbstract::TimeSamplingPtr iShapeTime,
172     const JobArgs & iArgs)
173 {
174     MStatus status;
175     mFileName = iFileName;
176     mAsOgawa = iAsOgawa;
177     mBoxIndex = 0;
178     mArgs = iArgs;
179     mShapeSamples = 1;
180     mTransSamples = 1;
181 
182     if (mArgs.useSelectionList)
183     {
184 
185         bool emptyDagPaths = mArgs.dagPaths.empty();
186 
187         // get the active selection
188         MSelectionList activeList;
189         MGlobal::getActiveSelectionList(activeList);
190         mSList = activeList;
191         unsigned int selectionSize = activeList.length();
192         for (unsigned int index = 0; index < selectionSize; index ++)
193         {
194             MDagPath dagPath;
195             status = activeList.getDagPath(index, dagPath);
196             if (status == MS::kSuccess)
197             {
198                 unsigned int length = dagPath.length();
199                 while (--length)
200                 {
201                     dagPath.pop();
202                     mSList.add(dagPath, MObject::kNullObj, true);
203                 }
204 
205                 if (emptyDagPaths)
206                 {
207                     mArgs.dagPaths.insert(dagPath);
208                 }
209             }
210         }
211     }
212 
213     mTransFrames = iTransFrames;
214     mShapeFrames = iShapeFrames;
215 
216     // only needed during creation of the transforms and shapes
217     mTransTime = iTransTime;
218     mTransTimeIndex = 0;
219     mShapeTime = iShapeTime;
220     mShapeTimeIndex = 0;
221 
222     // should have at least 1 value
223     assert(!mTransFrames.empty() && !mShapeFrames.empty());
224 
225     mFirstFrame = *(mTransFrames.begin());
226     std::set<double>::iterator last = mTransFrames.end();
227     last--;
228     mLastFrame = *last;
229     last = mShapeFrames.end();
230     last--;
231 
232     double lastShapeFrame = *last;
233     if (lastShapeFrame > mLastFrame)
234         mLastFrame = lastShapeFrame;
235 }
236 
getBoundingBox(double iFrame,const MMatrix & eMInvMat)237 MBoundingBox AbcWriteJob::getBoundingBox(double iFrame, const MMatrix & eMInvMat)
238 {
239     MStatus status;
240     MBoundingBox curBBox;
241 
242     if (iFrame == mFirstFrame)
243     {
244         // Set up bbox shape map in the first frame.
245         // If we have a lot of transforms and shapes, we don't need to
246         // iterate them for each frame.
247         MItDag dagIter;
248         for (dagIter.reset(mCurDag); !dagIter.isDone(); dagIter.next())
249         {
250             MObject object = dagIter.currentItem();
251             MDagPath path;
252             dagIter.getPath(path);
253 
254             // short-circuit if the selection flag is on but this node is not in the
255             // active selection
256 
257             // MGlobal::isSelected(ob) doesn't work, because DG node and DAG node is
258             // not the same even if they refer to the same MObject
259             if (mArgs.useSelectionList && !mSList.hasItem(path))
260             {
261                 dagIter.prune();
262                 continue;
263             }
264 
265             MFnDagNode dagNode(path, &status);
266             if (status == MS::kSuccess)
267             {
268                 // check for riCurves flag for flattening all curve object to
269                 // one curve group
270                 MPlug riCurvesPlug = dagNode.findPlug("riCurves", true, &status);
271                 if ( status == MS::kSuccess && riCurvesPlug.asBool() == true)
272                 {
273                     MBoundingBox box = dagNode.boundingBox();
274                     box.transformUsing(path.exclusiveMatrix()*eMInvMat);
275                     curBBox.expand(box);
276 
277                     // Prune this curve group
278                     dagIter.prune();
279 
280                     // Save children paths
281                     std::map< MDagPath, util::ShapeSet, util::cmpDag >::iterator iter =
282                         mBBoxShapeMap.insert(std::make_pair(mCurDag, util::ShapeSet())).first;
283                     if (iter != mBBoxShapeMap.end())
284                         (*iter).second.insert(path);
285                 }
286                 else if (object.hasFn(MFn::kParticle)
287                     || object.hasFn(MFn::kMesh)
288                     || object.hasFn(MFn::kNurbsCurve)
289                     || object.hasFn(MFn::kNurbsSurface) )
290                 {
291                     if (util::isIntermediate(object))
292                         continue;
293 
294                     MBoundingBox box = dagNode.boundingBox();
295                     box.transformUsing(path.exclusiveMatrix()*eMInvMat);
296                     curBBox.expand(box);
297 
298                     // Save children paths
299                     std::map< MDagPath, util::ShapeSet, util::cmpDag >::iterator iter =
300                         mBBoxShapeMap.insert(std::make_pair(mCurDag, util::ShapeSet())).first;
301                     if (iter != mBBoxShapeMap.end())
302                         (*iter).second.insert(path);
303                 }
304             }
305         }
306     }
307     else
308     {
309         // We have already find out all the shapes for the dag path.
310         std::map< MDagPath, util::ShapeSet, util::cmpDag >::iterator iter =
311             mBBoxShapeMap.find(mCurDag);
312         if (iter != mBBoxShapeMap.end())
313         {
314             // Iterate through the saved paths to calculate the box.
315             util::ShapeSet& paths = (*iter).second;
316             for (util::ShapeSet::iterator pathIter = paths.begin();
317                 pathIter != paths.end(); pathIter++)
318             {
319                 MFnDagNode dagNode(*pathIter, &status);
320                 if (status == MS::kSuccess)
321                 {
322                     MBoundingBox box = dagNode.boundingBox();
323                     box.transformUsing((*pathIter).exclusiveMatrix()*eMInvMat);
324                     curBBox.expand(box);
325                 }
326             }
327         }
328     }
329 
330     return curBBox;
331 }
332 
checkCurveGrp()333 bool AbcWriteJob::checkCurveGrp()
334 {
335     MItDag itDag(MItDag::kBreadthFirst, MFn::kNurbsCurve);
336     itDag.reset(mCurDag, MItDag::kBreadthFirst, MFn::kNurbsCurve);
337 
338     bool init = false;
339     int degree = 0;
340     MFnNurbsCurve::Form form = MFnNurbsCurve::kInvalid;
341     for (; !itDag.isDone(); itDag.next())
342     {
343         MDagPath curvePath;
344         if (itDag.getPath(curvePath) == MS::kSuccess)
345         {
346             MObject curve = curvePath.node();
347             if (!util::isIntermediate(curve) && curve.hasFn(MFn::kNurbsCurve))
348             {
349                 MFnNurbsCurve fn(curvePath);
350                 if (!init)
351                 {
352                     degree = fn.degree();
353                     form = fn.form();
354                     init = true;
355                 }
356                 else
357                 {
358                     if (degree != fn.degree() || form != fn.form())
359                         return false;
360                 }
361             }
362         }
363     }
364     return true;
365 }
366 
setup(double iFrame,MayaTransformWriterPtr iParent)367 void AbcWriteJob::setup(double iFrame, MayaTransformWriterPtr iParent)
368 {
369     MStatus status;
370 
371     // short-circuit if selection flag is on but this node isn't actively
372     // selected
373     if (mArgs.useSelectionList && !mSList.hasItem(mCurDag))
374         return;
375 
376     MObject ob = mCurDag.node();
377 
378     // skip all intermediate nodes (and their children)
379     if (util::isIntermediate(ob))
380     {
381         return;
382     }
383 
384     // skip nodes that aren't renderable (and their children)
385     if (mArgs.excludeInvisible && !util::isRenderable(ob))
386     {
387         return;
388     }
389 
390     // look for riCurves flag for flattening all curve objects to a curve group
391     MFnDependencyNode fnDepNode(ob, &status);
392     MPlug riCurvesPlug = fnDepNode.findPlug("riCurves", true, &status);
393     bool riCurvesVal = riCurvesPlug.asBool();
394     bool writeOutAsGroup = false;
395     if (riCurvesVal)
396     {
397         writeOutAsGroup = checkCurveGrp();
398         if (writeOutAsGroup == false)
399         {
400             MString msg = "Curves have different degrees or close ";
401             msg += "states, not writing out as curve group";
402             MGlobal::displayWarning(msg);
403         }
404     }
405     if ( status == MS::kSuccess && riCurvesVal && writeOutAsGroup)
406     {
407        if( !mArgs.writeCurvesGroup )
408        {
409            return;
410        }
411 
412         MayaNurbsCurveWriterPtr nurbsCurve;
413         if (iParent == NULL)
414         {
415             Alembic::Abc::OObject obj = mRoot.getTop();
416             nurbsCurve = MayaNurbsCurveWriterPtr(new MayaNurbsCurveWriter(
417                 mCurDag, obj, mShapeTimeIndex, true, mArgs));
418         }
419         else
420         {
421             Alembic::Abc::OObject obj = iParent->getObject();
422             nurbsCurve = MayaNurbsCurveWriterPtr(new MayaNurbsCurveWriter(
423                 mCurDag, obj, mShapeTimeIndex, true, mArgs));
424         }
425 
426         if (nurbsCurve->isAnimated() && mShapeTimeIndex != 0)
427         {
428             mCurveList.push_back(nurbsCurve);
429             mStats.mCurveAnimNum++;
430             mStats.mCurveAnimCurves += nurbsCurve->getNumCurves();
431             mStats.mCurveAnimCVs += nurbsCurve->getNumCVs();
432         }
433         else
434         {
435             mStats.mCurveStaticNum++;
436             mStats.mCurveStaticCurves += nurbsCurve->getNumCurves();
437             mStats.mCurveStaticCVs += nurbsCurve->getNumCVs();
438         }
439 
440         AttributesWriterPtr attrs = nurbsCurve->getAttrs();
441         if (mShapeTimeIndex != 0 && attrs->isAnimated())
442             mShapeAttrList.push_back(attrs);
443     }
444     else if (ob.hasFn(MFn::kTransform))
445     {
446        MayaTransformWriterPtr trans;
447 
448         MFnTransform fnTrans(ob, &status);
449         if (status != MS::kSuccess)
450         {
451             MString msg = "Initialize transform node ";
452            msg += mCurDag.fullPathName();
453            msg += " failed, skipping.";
454            MGlobal::displayWarning(msg);
455             return;
456         }
457 
458         // parented to the root case
459         if (iParent == NULL)
460         {
461             Alembic::Abc::OObject obj = mRoot.getTop();
462             trans = MayaTransformWriterPtr(new MayaTransformWriter(
463                 obj, mCurDag, mTransTimeIndex, mArgs));
464         }
465         else
466         {
467             trans = MayaTransformWriterPtr(new MayaTransformWriter(
468                 *iParent, mCurDag, mTransTimeIndex, mArgs));
469         }
470 
471         if (trans->isAnimated() && mTransTimeIndex != 0)
472         {
473            mTransList.push_back(trans);
474             mStats.mTransAnimNum++;
475         }
476         else
477         {
478             mStats.mTransStaticNum++;
479         }
480 
481         AttributesWriterPtr attrs = trans->getAttrs();
482         if (mTransTimeIndex != 0 && attrs->isAnimated())
483             mTransAttrList.push_back(attrs);
484 
485         // loop through the children, making sure to push and pop them
486         // from the MDagPath
487         unsigned int numChild = mCurDag.childCount();
488         for (unsigned int i = 0; i < numChild; ++i)
489         {
490             if (mCurDag.push(mCurDag.child(i)) == MS::kSuccess)
491             {
492                 setup(iFrame, trans);
493                 mCurDag.pop();
494             }
495         }
496     }
497     else if (ob.hasFn(MFn::kLocator))
498     {
499        if( !mArgs.writeLocators )
500        {
501            return;
502        }
503 
504         MFnDependencyNode fnLocator(ob, & status);
505         if (status != MS::kSuccess)
506         {
507             MString msg = "Initialize locator node ";
508             msg += mCurDag.fullPathName();
509             msg += " failed, skipping.";
510             MGlobal::displayWarning(msg);
511             return;
512         }
513 
514         if (iParent != NULL)
515         {
516             Alembic::Abc::OObject obj = iParent->getObject();
517             MayaLocatorWriterPtr locator(new MayaLocatorWriter(
518                 mCurDag, obj, mShapeTimeIndex, mArgs));
519 
520             if (locator->isAnimated() && mShapeTimeIndex != 0)
521             {
522                 mLocatorList.push_back(locator);
523                 mStats.mLocatorAnimNum++;
524             }
525             else
526             {
527                 mStats.mLocatorStaticNum++;
528             }
529 
530             AttributesWriterPtr attrs = locator->getAttrs();
531             if (mShapeTimeIndex != 0 && attrs->isAnimated())
532                 mShapeAttrList.push_back(attrs);
533         }
534         else
535         {
536             MString err = "Can't translate ";
537             err += fnLocator.name() + " since it doesn't have a parent.";
538             MGlobal::displayError(err);
539         }
540     }
541     else if (ob.hasFn(MFn::kParticle))
542     {
543        if( !mArgs.writeParticles )
544        {
545            return;
546        }
547 
548         MFnParticleSystem mFnParticle(ob, &status);
549         if (status != MS::kSuccess)
550         {
551             MString msg = "Initialize particle system ";
552             msg += mCurDag.fullPathName();
553             msg += " failed, skipping.";
554             MGlobal::displayWarning(msg);
555             return;
556         }
557 
558         if (iParent != NULL)
559         {
560             Alembic::Abc::OObject obj = iParent->getObject();
561             MayaPointPrimitiveWriterPtr particle(new MayaPointPrimitiveWriter(
562                 iFrame, mCurDag, obj, mShapeTimeIndex, mArgs));
563 
564             if (particle->isAnimated() && mShapeTimeIndex != 0)
565             {
566                 mPointList.push_back(particle);
567                 mStats.mPointAnimNum++;
568                 mStats.mPointAnimCVs += particle->getNumCVs();
569             }
570             else
571             {
572                 mStats.mPointStaticNum++;
573                 mStats.mPointStaticCVs += particle->getNumCVs();
574             }
575 
576             AttributesWriterPtr attrs = particle->getAttrs();
577             if (mShapeTimeIndex != 0 && attrs->isAnimated())
578                 mShapeAttrList.push_back(attrs);
579         }
580         else
581         {
582             MString err = "Can't translate ";
583             err += mFnParticle.name() + " since it doesn't have a parent.";
584             MGlobal::displayError(err);
585         }
586     }
587     else if (ob.hasFn(MFn::kMesh))
588     {
589        if( !mArgs.writeMeshes)
590        {
591            return;
592        }
593 
594         MFnMesh fnMesh(ob, &status);
595         if (status != MS::kSuccess)
596         {
597             MString msg = "Initialize mesh node ";
598             msg += mCurDag.fullPathName();
599             msg += " failed, skipping.";
600             MGlobal::displayWarning(msg);
601             return;
602         }
603 
604         if (iParent != NULL)
605         {
606             Alembic::Abc::OObject obj = iParent->getObject();
607             MayaMeshWriterPtr mesh(new MayaMeshWriter(mCurDag, obj,
608                 mShapeTimeIndex, mArgs));
609 
610             if (mesh->isAnimated() && mShapeTimeIndex != 0)
611             {
612                 mMeshList.push_back(mesh);
613                 if (mesh->isSubD())
614                 {
615                     mStats.mSubDAnimNum++;
616                     mStats.mSubDAnimCVs += mesh->getNumCVs();
617                     mStats.mSubDAnimFaces += mesh->getNumFaces();
618                 }
619                 else
620                 {
621                     mStats.mPolyAnimNum++;
622                     mStats.mPolyAnimCVs += mesh->getNumCVs();
623                     mStats.mPolyAnimFaces += mesh->getNumFaces();
624                 }
625             }
626             else
627             {
628                 if (mesh->isSubD())
629                 {
630                     mStats.mSubDStaticNum++;
631                     mStats.mSubDStaticCVs += mesh->getNumCVs();
632                     mStats.mSubDStaticFaces += mesh->getNumFaces();
633                 }
634                 else
635                 {
636                     mStats.mPolyStaticNum++;
637                     mStats.mPolyStaticCVs += mesh->getNumCVs();
638                     mStats.mPolyStaticFaces += mesh->getNumFaces();
639                 }
640             }
641 
642             AttributesWriterPtr attrs = mesh->getAttrs();
643             if (mShapeTimeIndex != 0 && attrs->isAnimated())
644                 mShapeAttrList.push_back(attrs);
645 
646             if (mShapeTimeIndex != 0)
647             {
648                 std::vector< MayaFaceSetWriterPtr >::iterator it;
649                 for (it = mesh->beginFaces(); it != mesh->endFaces(); ++it)
650                 {
651                     if ((*it)->getAttrs() && (*it)->getAttrs()->isAnimated())
652                     {
653                         mShapeAttrList.push_back((*it)->getAttrs());
654                     }
655                 }
656             }
657         }
658         else
659         {
660             MString err = "Can't translate ";
661             err += fnMesh.name() + " since it doesn't have a parent.";
662             MGlobal::displayError(err);
663         }
664     }
665     else if (ob.hasFn(MFn::kCamera))
666     {
667        if( !mArgs.writeCameras )
668        {
669            return;
670        }
671 
672         MFnCamera fnCamera(ob, &status);
673         if (status != MS::kSuccess)
674         {
675             MString msg = "Initialize camera node ";
676             msg += mCurDag.fullPathName();
677             msg += " failed, skipping.";
678             MGlobal::displayWarning(msg);
679             return;
680         }
681 
682         if (iParent != NULL)
683         {
684             Alembic::Abc::OObject obj = iParent->getObject();
685             MayaCameraWriterPtr camera(new MayaCameraWriter(
686                 mCurDag, obj, mShapeTimeIndex, mArgs));
687 
688             if (camera->isAnimated() && mShapeTimeIndex != 0)
689             {
690                 mCameraList.push_back(camera);
691                 mStats.mCameraAnimNum++;
692             }
693             else
694                 mStats.mCameraStaticNum++;
695 
696             AttributesWriterPtr attrs = camera->getAttrs();
697             if (mShapeTimeIndex != 0 && attrs->isAnimated())
698                 mShapeAttrList.push_back(attrs);
699         }
700         else
701         {
702             MString err = "Can't translate ";
703             err += fnCamera.name() + " since it doesn't have a parent.";
704             MGlobal::displayError(err);
705         }
706     }
707     else if (ob.hasFn(MFn::kNurbsSurface))
708     {
709        if( !mArgs.writeNurbsSurfaces )
710        {
711            return;
712        }
713 
714         MFnNurbsSurface fnNurbsSurface(ob, &status);
715         if (status != MS::kSuccess)
716         {
717             MString msg = "Initialize nurbs surface ";
718             msg += mCurDag.fullPathName();
719             msg += " failed, skipping.";
720             MGlobal::displayWarning(msg);
721             return;
722         }
723 
724         if (iParent != NULL)
725         {
726             Alembic::Abc::OObject obj = iParent->getObject();
727             MayaNurbsSurfaceWriterPtr nurbsSurface(new MayaNurbsSurfaceWriter(
728                 mCurDag, obj,  mShapeTimeIndex, mArgs));
729 
730             if (nurbsSurface->isAnimated() && mShapeTimeIndex != 0)
731             {
732                 mNurbsList.push_back(nurbsSurface);
733                 mStats.mNurbsAnimNum++;
734                 mStats.mNurbsAnimCVs += nurbsSurface->getNumCVs();
735             }
736             else
737             {
738                 mStats.mNurbsStaticNum++;
739                 mStats.mNurbsStaticCVs += nurbsSurface->getNumCVs();
740             }
741 
742             AttributesWriterPtr attrs = nurbsSurface->getAttrs();
743             if (mShapeTimeIndex != 0 && attrs->isAnimated())
744                 mShapeAttrList.push_back(attrs);
745         }
746         else
747         {
748             MString err = "Can't translate ";
749             err += fnNurbsSurface.name() + " since it doesn't have a parent.";
750             MGlobal::displayError(err);
751         }
752     }
753     else if (ob.hasFn(MFn::kNurbsCurve))
754     {
755        if( !mArgs.writeNurbsCurves )
756        {
757            return;
758        }
759 
760         MFnNurbsCurve fnNurbsCurve(ob, &status);
761         if (status != MS::kSuccess)
762         {
763             MString msg = "Initialize curve node ";
764             msg += mCurDag.fullPathName();
765             msg += " failed, skipping.";
766             MGlobal::displayWarning(msg);
767             return;
768         }
769 
770         if (iParent != NULL)
771         {
772             Alembic::Abc::OObject obj = iParent->getObject();
773             MayaNurbsCurveWriterPtr nurbsCurve(new MayaNurbsCurveWriter(
774                 mCurDag, obj, mShapeTimeIndex, false, mArgs));
775 
776             if (nurbsCurve->isAnimated() && mShapeTimeIndex != 0)
777             {
778                 mCurveList.push_back(nurbsCurve);
779                 mStats.mCurveAnimNum++;
780                 mStats.mCurveAnimCurves++;
781                 mStats.mCurveAnimCVs += nurbsCurve->getNumCVs();
782             }
783             else
784             {
785                 mStats.mCurveStaticNum++;
786                 mStats.mCurveStaticCurves++;
787                 mStats.mCurveStaticCVs += nurbsCurve->getNumCVs();
788             }
789 
790             AttributesWriterPtr attrs = nurbsCurve->getAttrs();
791             if (mShapeTimeIndex != 0 && attrs->isAnimated())
792                 mShapeAttrList.push_back(attrs);
793         }
794         else
795         {
796             MString err = "Can't translate ";
797             err += fnNurbsCurve.name() + " since it doesn't have a parent.";
798             MGlobal::displayError(err);
799         }
800     }
801     else
802     {
803         MString warn = mCurDag.fullPathName() + " is an unsupported type of ";
804         warn += ob.apiTypeStr();
805         MGlobal::displayWarning(warn);
806     }
807 }
808 
809 
~AbcWriteJob()810 AbcWriteJob::~AbcWriteJob()
811 {
812 }
813 
eval(double iFrame)814 bool AbcWriteJob::eval(double iFrame)
815 {
816     if (iFrame == mFirstFrame)
817     {
818         // check if the shortnames of any two nodes are the same
819         // if so, exit here
820         hasDuplicates(mArgs.dagPaths, mArgs.stripNamespace);
821 
822         std::string appWriter = "Maya ";
823         appWriter += MGlobal::mayaVersion().asChar();
824         appWriter += " AbcExport v";
825         appWriter += ABCEXPORT_VERSION;
826 
827         std::string userInfo = "Exported from: ";
828         userInfo += MFileIO::currentFile().asChar();
829 
830         // these symbols can't be in the meta data
831         if (userInfo.find('=') != std::string::npos ||
832             userInfo.find(';') != std::string::npos)
833         {
834             userInfo = "";
835         }
836 
837         MTime sec(1.0, MTime::kSeconds);
838         double fps(sec.as(MTime::uiUnit()));
839 
840 #ifdef ALEMBIC_WITH_HDF5
841         if (mAsOgawa)
842         {
843             mRoot = CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(),
844                 mFileName, fps, appWriter, userInfo,
845                 Alembic::Abc::ErrorHandler::kThrowPolicy);
846         }
847         else
848         {
849             mRoot = CreateArchiveWithInfo(Alembic::AbcCoreHDF5::WriteArchive(),
850                 mFileName, fps, appWriter, userInfo,
851                 Alembic::Abc::ErrorHandler::kThrowPolicy);
852         }
853 #else
854         // just write it out as Ogawa
855         mRoot = CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(),
856             mFileName, fps, appWriter, userInfo,
857             Alembic::Abc::ErrorHandler::kThrowPolicy);
858 #endif
859 
860         mShapeTimeIndex = mRoot.addTimeSampling(*mShapeTime);
861         mTransTimeIndex = mRoot.addTimeSampling(*mTransTime);
862 
863         mBoxProp =  Alembic::AbcGeom::CreateOArchiveBounds(mRoot,
864             mTransTimeIndex);
865 
866         if (!mRoot.valid())
867         {
868             std::string theError = "Unable to create abc file";
869             throw std::runtime_error(theError);
870         }
871 
872         mArgs.setFirstAnimShape = (iFrame == *mShapeFrames.begin());
873 
874         util::ShapeSet::const_iterator end = mArgs.dagPaths.end();
875         for (util::ShapeSet::const_iterator it = mArgs.dagPaths.begin();
876             it != end; ++it)
877         {
878             mCurDag = *it;
879             setup(iFrame * util::spf(), MayaTransformWriterPtr());
880         }
881         perFrameCallback(iFrame);
882     }
883     else
884     {
885         std::set<double>::iterator checkFrame = mShapeFrames.find(iFrame);
886         bool foundShapeFrame = false;
887         if (checkFrame != mShapeFrames.end())
888         {
889             assert(mRoot != NULL);
890             foundShapeFrame = true;
891             mShapeSamples ++;
892             double curTime = iFrame * util::spf();
893 
894             std::vector< MayaCameraWriterPtr >::iterator camIt, camEnd;
895             camEnd = mCameraList.end();
896             for (camIt = mCameraList.begin(); camIt != camEnd; camIt++)
897             {
898                 (*camIt)->write();
899             }
900 
901             std::vector< MayaMeshWriterPtr >::iterator meshIt, meshEnd;
902             meshEnd = mMeshList.end();
903             for (meshIt = mMeshList.begin(); meshIt != meshEnd; meshIt++)
904             {
905                 (*meshIt)->write();
906                 if ((*meshIt)->isSubD())
907                 {
908                     mStats.mSubDAnimCVs += (*meshIt)->getNumCVs();
909                 }
910                 else
911                 {
912                     mStats.mPolyAnimCVs += (*meshIt)->getNumCVs();
913                 }
914             }
915 
916             std::vector< MayaNurbsCurveWriterPtr >::iterator curveIt, curveEnd;
917             curveEnd = mCurveList.end();
918             for (curveIt = mCurveList.begin(); curveIt != curveEnd; curveIt++)
919             {
920                 (*curveIt)->write();
921                 mStats.mCurveAnimCVs += (*curveIt)->getNumCVs();
922             }
923 
924             std::vector< MayaNurbsSurfaceWriterPtr >::iterator nurbsIt,nurbsEnd;
925             nurbsEnd = mNurbsList.end();
926             for (nurbsIt = mNurbsList.begin(); nurbsIt != nurbsEnd; nurbsIt++)
927             {
928                 (*nurbsIt)->write();
929                 mStats.mNurbsAnimCVs += (*nurbsIt)->getNumCVs();
930             }
931 
932             std::vector< MayaLocatorWriterPtr >::iterator locIt, locEnd;
933             locEnd = mLocatorList.end();
934             for (locIt = mLocatorList.begin(); locIt != locEnd; locIt++)
935             {
936                 (*locIt)->write();
937             }
938 
939             std::vector< MayaPointPrimitiveWriterPtr >::iterator ptIt, ptEnd;
940             ptEnd = mPointList.end();
941             for (ptIt = mPointList.begin(); ptIt != ptEnd; ptIt++)
942             {
943                 (*ptIt)->write(curTime);
944                 mStats.mPointAnimCVs += (*ptIt)->getNumCVs();
945             }
946 
947             std::vector< AttributesWriterPtr >::iterator sattrCur =
948                 mShapeAttrList.begin();
949 
950             std::vector< AttributesWriterPtr >::iterator sattrEnd =
951                 mShapeAttrList.end();
952 
953             for(; sattrCur != sattrEnd; sattrCur++)
954             {
955                 (*sattrCur)->write();
956             }
957         }
958 
959         checkFrame = mTransFrames.find(iFrame);
960         bool foundTransFrame = false;
961         if (checkFrame != mTransFrames.end())
962         {
963             assert(mRoot.valid());
964             foundTransFrame = true;
965             mTransSamples ++;
966             std::vector< MayaTransformWriterPtr >::iterator tcur =
967                 mTransList.begin();
968 
969             std::vector< MayaTransformWriterPtr >::iterator tend =
970                 mTransList.end();
971 
972             for (; tcur != tend; tcur++)
973             {
974                 (*tcur)->write();
975             }
976 
977             std::vector< AttributesWriterPtr >::iterator tattrCur =
978                 mTransAttrList.begin();
979 
980             std::vector< AttributesWriterPtr >::iterator tattrEnd =
981                 mTransAttrList.end();
982 
983             for(; tattrCur != tattrEnd; tattrCur++)
984             {
985                 (*tattrCur)->write();
986             }
987         }
988 
989         if (foundTransFrame || foundShapeFrame)
990             perFrameCallback(iFrame);
991     }
992 
993     if (iFrame == mLastFrame)
994     {
995         postCallback(iFrame);
996         return true;
997     }
998 
999     return false;
1000 }
1001 
perFrameCallback(double iFrame)1002 void AbcWriteJob::perFrameCallback(double iFrame)
1003 {
1004     MBoundingBox bbox;
1005 
1006     util::ShapeSet::iterator it = mArgs.dagPaths.begin();
1007     const util::ShapeSet::iterator end = mArgs.dagPaths.end();
1008     for (; it != end; it ++)
1009     {
1010         mCurDag = *it;
1011 
1012         MMatrix eMInvMat;
1013         if (mArgs.worldSpace)
1014         {
1015             eMInvMat.setToIdentity();
1016         }
1017         else
1018         {
1019             eMInvMat = mCurDag.exclusiveMatrixInverse();
1020         }
1021 
1022         bbox.expand(getBoundingBox(iFrame, eMInvMat));
1023     }
1024 
1025     Alembic::Abc::V3d min(bbox.min().x, bbox.min().y, bbox.min().z);
1026     Alembic::Abc::V3d max(bbox.max().x, bbox.max().y, bbox.max().z);
1027     Alembic::Abc::Box3d b(min, max);
1028     mBoxProp.set(b);
1029 
1030     processCallback(mArgs.melPerFrameCallback, true, iFrame, bbox);
1031     processCallback(mArgs.pythonPerFrameCallback, false, iFrame, bbox);
1032 }
1033 
1034 
1035 // write the frame ranges and statistic string on the root
1036 // Also call the post callbacks
postCallback(double iFrame)1037 void AbcWriteJob::postCallback(double iFrame)
1038 {
1039     std::string statsStr = "";
1040 
1041     addToString(statsStr, "SubDStaticNum", mStats.mSubDStaticNum);
1042     addToString(statsStr, "SubDAnimNum", mStats.mSubDAnimNum);
1043     addToString(statsStr, "SubDStaticCVs", mStats.mSubDStaticCVs);
1044     addToString(statsStr, "SubDAnimCVs", mStats.mSubDAnimCVs);
1045     addToString(statsStr, "SubDStaticFaces", mStats.mSubDStaticFaces);
1046     addToString(statsStr, "SubDAnimFaces", mStats.mSubDAnimFaces);
1047 
1048     addToString(statsStr, "PolyStaticNum", mStats.mPolyStaticNum);
1049     addToString(statsStr, "PolyAnimNum", mStats.mPolyAnimNum);
1050     addToString(statsStr, "PolyStaticCVs", mStats.mPolyStaticCVs);
1051     addToString(statsStr, "PolyAnimCVs", mStats.mPolyAnimCVs);
1052     addToString(statsStr, "PolyStaticFaces", mStats.mPolyStaticFaces);
1053     addToString(statsStr, "PolyAnimFaces", mStats.mPolyAnimFaces);
1054 
1055     addToString(statsStr, "CurveStaticNum", mStats.mCurveStaticNum);
1056     addToString(statsStr, "CurveStaticCurves", mStats.mCurveStaticCurves);
1057     addToString(statsStr, "CurveAnimNum", mStats.mCurveAnimNum);
1058     addToString(statsStr, "CurveAnimCurves", mStats.mCurveAnimCurves);
1059     addToString(statsStr, "CurveStaticCVs", mStats.mCurveStaticCVs);
1060     addToString(statsStr, "CurveAnimCVs", mStats.mCurveAnimCVs);
1061 
1062     addToString(statsStr, "PointStaticNum", mStats.mPointStaticNum);
1063     addToString(statsStr, "PointAnimNum", mStats.mPointAnimNum);
1064     addToString(statsStr, "PointStaticCVs", mStats.mPointStaticCVs);
1065     addToString(statsStr, "PointAnimCVs", mStats.mPointAnimCVs);
1066 
1067     addToString(statsStr, "NurbsStaticNum", mStats.mNurbsStaticNum);
1068     addToString(statsStr, "NurbsAnimNum", mStats.mNurbsAnimNum);
1069     addToString(statsStr, "NurbsStaticCVs", mStats.mNurbsStaticCVs);
1070     addToString(statsStr, "NurbsAnimCVs", mStats.mNurbsAnimCVs);
1071 
1072     addToString(statsStr, "TransStaticNum", mStats.mTransStaticNum);
1073     addToString(statsStr, "TransAnimNum", mStats.mTransAnimNum);
1074 
1075     addToString(statsStr, "LocatorStaticNum", mStats.mLocatorStaticNum);
1076     addToString(statsStr, "LocatorAnimNum", mStats.mLocatorAnimNum);
1077 
1078     addToString(statsStr, "CameraStaticNum", mStats.mCameraStaticNum);
1079     addToString(statsStr, "CameraAnimNum", mStats.mCameraAnimNum);
1080 
1081     if (statsStr.length() > 0)
1082     {
1083         Alembic::Abc::OStringProperty stats(mRoot.getTop().getProperties(),
1084             "statistics");
1085         stats.set(statsStr);
1086     }
1087 
1088     if (mTransTimeIndex != 0)
1089     {
1090         MString propName;
1091         propName += static_cast<int>(mTransTimeIndex);
1092         propName += ".samples";
1093         Alembic::Abc::OUInt32Property samp(mRoot.getTop().getProperties(),
1094             propName.asChar());
1095         samp.set(mTransSamples);
1096     }
1097 
1098     if (mShapeTimeIndex != 0 && mShapeTimeIndex != mTransTimeIndex)
1099     {
1100         MString propName;
1101         propName += static_cast<int>(mShapeTimeIndex);
1102         propName += ".samples";
1103         Alembic::Abc::OUInt32Property samp(mRoot.getTop().getProperties(),
1104             propName.asChar());
1105         samp.set(mShapeSamples);
1106     }
1107 
1108     MBoundingBox bbox;
1109 
1110     if (mArgs.melPostCallback.find("#BOUNDS#") != std::string::npos ||
1111         mArgs.pythonPostCallback.find("#BOUNDS#") != std::string::npos ||
1112         mArgs.melPostCallback.find("#BOUNDSARRAY#") != std::string::npos ||
1113         mArgs.pythonPostCallback.find("#BOUNDSARRAY#") != std::string::npos)
1114     {
1115         util::ShapeSet::const_iterator it = mArgs.dagPaths.begin();
1116         const util::ShapeSet::const_iterator end = mArgs.dagPaths.end();
1117         for (; it != end; it ++)
1118         {
1119             mCurDag = *it;
1120 
1121             MMatrix eMInvMat;
1122             if (mArgs.worldSpace)
1123             {
1124                 eMInvMat.setToIdentity();
1125             }
1126             else
1127             {
1128                 eMInvMat = mCurDag.exclusiveMatrixInverse();
1129             }
1130 
1131             bbox.expand(getBoundingBox(iFrame, eMInvMat));
1132         }
1133     }
1134 
1135     processCallback(mArgs.melPostCallback, true, iFrame, bbox);
1136     processCallback(mArgs.pythonPostCallback, false, iFrame, bbox);
1137 }
1138 
1139 
1140