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