1140 lines
36 KiB
C++
1140 lines
36 KiB
C++
//-*****************************************************************************
|
|
//
|
|
// Copyright (c) 2009-2014,
|
|
// Sony Pictures Imageworks Inc. and
|
|
// Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Sony Pictures Imageworks, nor
|
|
// Industrial Light & Magic, nor the names of their contributors may be used
|
|
// to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
//-*****************************************************************************
|
|
|
|
#include "AbcWriteJob.h"
|
|
|
|
#ifdef ALEMBIC_WITH_HDF5
|
|
#include <Alembic/AbcCoreHDF5/All.h>
|
|
#endif
|
|
|
|
#include <Alembic/AbcCoreOgawa/All.h>
|
|
namespace
|
|
{
|
|
void hasDuplicates(const util::ShapeSet & dagPath, unsigned int stripDepth)
|
|
{
|
|
std::map<std::string, MDagPath> roots;
|
|
const util::ShapeSet::const_iterator end = dagPath.end();
|
|
for (util::ShapeSet::const_iterator it = dagPath.begin();
|
|
it != end; it++)
|
|
{
|
|
std::string fullName = it->fullPathName().asChar();
|
|
if (!fullName.empty() && fullName[0] == '|')
|
|
{
|
|
fullName = fullName.substr(1);
|
|
}
|
|
|
|
if (stripDepth > 0)
|
|
{
|
|
MString name = fullName.c_str();
|
|
fullName = util::stripNamespaces(name, stripDepth).asChar();
|
|
}
|
|
|
|
std::map<std::string, MDagPath>::iterator strIt =
|
|
roots.find(fullName);
|
|
if (strIt != roots.end())
|
|
{
|
|
std::string theError = "Conflicting root node names specified: ";
|
|
theError += it->fullPathName().asChar();
|
|
theError += " ";
|
|
theError += strIt->second.fullPathName().asChar();
|
|
if (stripDepth > 0)
|
|
{
|
|
theError += " with -stripNamespace specified.";
|
|
}
|
|
throw std::runtime_error(theError);
|
|
}
|
|
else
|
|
{
|
|
roots[fullName] = *it;
|
|
}
|
|
}
|
|
}
|
|
|
|
void addToString(std::string & str,
|
|
const std::string & name, unsigned int value)
|
|
{
|
|
if (value > 0)
|
|
{
|
|
std::stringstream ss;
|
|
ss << value;
|
|
str += name + std::string(" ") + ss.str() + std::string(" ");
|
|
}
|
|
}
|
|
|
|
void processCallback(std::string iCallback, bool isMelCallback,
|
|
double iFrame, const MBoundingBox & iBbox)
|
|
{
|
|
if (iCallback.empty())
|
|
return;
|
|
|
|
size_t pos = iCallback.find("#FRAME#");
|
|
if ( pos != std::string::npos )
|
|
{
|
|
std::stringstream sstrm;
|
|
sstrm.precision(std::numeric_limits<double>::digits10);
|
|
sstrm << iFrame;
|
|
std::string str = sstrm.str();
|
|
|
|
iCallback.replace(pos, 7, str);
|
|
}
|
|
|
|
pos = iCallback.find("#BOUNDS#");
|
|
if ( pos != std::string::npos )
|
|
{
|
|
std::stringstream sstrm;
|
|
sstrm.precision(std::numeric_limits<float>::digits10);
|
|
sstrm << " " << iBbox.min().x << " " << iBbox.min().y << " " <<
|
|
iBbox.min().z << " " << iBbox.max().x << " " <<
|
|
iBbox.max().y << " " <<iBbox.max().z;
|
|
|
|
std::string str = sstrm.str();
|
|
|
|
iCallback.replace(pos, 8, str);
|
|
}
|
|
|
|
pos = iCallback.find("#BOUNDSARRAY#");
|
|
if ( pos != std::string::npos )
|
|
{
|
|
std::stringstream sstrm;
|
|
sstrm.precision(std::numeric_limits<float>::digits10);
|
|
|
|
if (isMelCallback)
|
|
{
|
|
sstrm << " {";
|
|
}
|
|
else
|
|
{
|
|
sstrm << " [";
|
|
}
|
|
|
|
sstrm << iBbox.min().x << "," << iBbox.min().y << "," <<
|
|
iBbox.min().z << "," << iBbox.max().x << "," <<
|
|
iBbox.max().y << "," << iBbox.max().z;
|
|
|
|
if (isMelCallback)
|
|
{
|
|
sstrm << "} ";
|
|
}
|
|
else
|
|
{
|
|
sstrm << "] ";
|
|
}
|
|
std::string str = sstrm.str();
|
|
iCallback.replace(pos, 13, str);
|
|
}
|
|
|
|
if (isMelCallback)
|
|
MGlobal::executeCommand(iCallback.c_str(), true);
|
|
else
|
|
MGlobal::executePythonCommand(iCallback.c_str(), true);
|
|
}
|
|
}
|
|
|
|
AbcWriteJob::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)
|
|
{
|
|
MStatus status;
|
|
mFileName = iFileName;
|
|
mAsOgawa = iAsOgawa;
|
|
mBoxIndex = 0;
|
|
mArgs = iArgs;
|
|
mShapeSamples = 1;
|
|
mTransSamples = 1;
|
|
|
|
if (mArgs.useSelectionList)
|
|
{
|
|
|
|
bool emptyDagPaths = mArgs.dagPaths.empty();
|
|
|
|
// get the active selection
|
|
MSelectionList activeList;
|
|
MGlobal::getActiveSelectionList(activeList);
|
|
mSList = activeList;
|
|
unsigned int selectionSize = activeList.length();
|
|
for (unsigned int index = 0; index < selectionSize; index ++)
|
|
{
|
|
MDagPath dagPath;
|
|
status = activeList.getDagPath(index, dagPath);
|
|
if (status == MS::kSuccess)
|
|
{
|
|
unsigned int length = dagPath.length();
|
|
while (--length)
|
|
{
|
|
dagPath.pop();
|
|
mSList.add(dagPath, MObject::kNullObj, true);
|
|
}
|
|
|
|
if (emptyDagPaths)
|
|
{
|
|
mArgs.dagPaths.insert(dagPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mTransFrames = iTransFrames;
|
|
mShapeFrames = iShapeFrames;
|
|
|
|
// only needed during creation of the transforms and shapes
|
|
mTransTime = iTransTime;
|
|
mTransTimeIndex = 0;
|
|
mShapeTime = iShapeTime;
|
|
mShapeTimeIndex = 0;
|
|
|
|
// should have at least 1 value
|
|
assert(!mTransFrames.empty() && !mShapeFrames.empty());
|
|
|
|
mFirstFrame = *(mTransFrames.begin());
|
|
std::set<double>::iterator last = mTransFrames.end();
|
|
last--;
|
|
mLastFrame = *last;
|
|
last = mShapeFrames.end();
|
|
last--;
|
|
|
|
double lastShapeFrame = *last;
|
|
if (lastShapeFrame > mLastFrame)
|
|
mLastFrame = lastShapeFrame;
|
|
}
|
|
|
|
MBoundingBox AbcWriteJob::getBoundingBox(double iFrame, const MMatrix & eMInvMat)
|
|
{
|
|
MStatus status;
|
|
MBoundingBox curBBox;
|
|
|
|
if (iFrame == mFirstFrame)
|
|
{
|
|
// Set up bbox shape map in the first frame.
|
|
// If we have a lot of transforms and shapes, we don't need to
|
|
// iterate them for each frame.
|
|
MItDag dagIter;
|
|
for (dagIter.reset(mCurDag); !dagIter.isDone(); dagIter.next())
|
|
{
|
|
MObject object = dagIter.currentItem();
|
|
MDagPath path;
|
|
dagIter.getPath(path);
|
|
|
|
// short-circuit if the selection flag is on but this node is not in the
|
|
// active selection
|
|
|
|
// MGlobal::isSelected(ob) doesn't work, because DG node and DAG node is
|
|
// not the same even if they refer to the same MObject
|
|
if (mArgs.useSelectionList && !mSList.hasItem(path))
|
|
{
|
|
dagIter.prune();
|
|
continue;
|
|
}
|
|
|
|
MFnDagNode dagNode(path, &status);
|
|
if (status == MS::kSuccess)
|
|
{
|
|
// check for riCurves flag for flattening all curve object to
|
|
// one curve group
|
|
MPlug riCurvesPlug = dagNode.findPlug("riCurves", true, &status);
|
|
if ( status == MS::kSuccess && riCurvesPlug.asBool() == true)
|
|
{
|
|
MBoundingBox box = dagNode.boundingBox();
|
|
box.transformUsing(path.exclusiveMatrix()*eMInvMat);
|
|
curBBox.expand(box);
|
|
|
|
// Prune this curve group
|
|
dagIter.prune();
|
|
|
|
// Save children paths
|
|
std::map< MDagPath, util::ShapeSet, util::cmpDag >::iterator iter =
|
|
mBBoxShapeMap.insert(std::make_pair(mCurDag, util::ShapeSet())).first;
|
|
if (iter != mBBoxShapeMap.end())
|
|
(*iter).second.insert(path);
|
|
}
|
|
else if (object.hasFn(MFn::kParticle)
|
|
|| object.hasFn(MFn::kMesh)
|
|
|| object.hasFn(MFn::kNurbsCurve)
|
|
|| object.hasFn(MFn::kNurbsSurface) )
|
|
{
|
|
if (util::isIntermediate(object))
|
|
continue;
|
|
|
|
MBoundingBox box = dagNode.boundingBox();
|
|
box.transformUsing(path.exclusiveMatrix()*eMInvMat);
|
|
curBBox.expand(box);
|
|
|
|
// Save children paths
|
|
std::map< MDagPath, util::ShapeSet, util::cmpDag >::iterator iter =
|
|
mBBoxShapeMap.insert(std::make_pair(mCurDag, util::ShapeSet())).first;
|
|
if (iter != mBBoxShapeMap.end())
|
|
(*iter).second.insert(path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We have already find out all the shapes for the dag path.
|
|
std::map< MDagPath, util::ShapeSet, util::cmpDag >::iterator iter =
|
|
mBBoxShapeMap.find(mCurDag);
|
|
if (iter != mBBoxShapeMap.end())
|
|
{
|
|
// Iterate through the saved paths to calculate the box.
|
|
util::ShapeSet& paths = (*iter).second;
|
|
for (util::ShapeSet::iterator pathIter = paths.begin();
|
|
pathIter != paths.end(); pathIter++)
|
|
{
|
|
MFnDagNode dagNode(*pathIter, &status);
|
|
if (status == MS::kSuccess)
|
|
{
|
|
MBoundingBox box = dagNode.boundingBox();
|
|
box.transformUsing((*pathIter).exclusiveMatrix()*eMInvMat);
|
|
curBBox.expand(box);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return curBBox;
|
|
}
|
|
|
|
bool AbcWriteJob::checkCurveGrp()
|
|
{
|
|
MItDag itDag(MItDag::kBreadthFirst, MFn::kNurbsCurve);
|
|
itDag.reset(mCurDag, MItDag::kBreadthFirst, MFn::kNurbsCurve);
|
|
|
|
bool init = false;
|
|
int degree = 0;
|
|
MFnNurbsCurve::Form form = MFnNurbsCurve::kInvalid;
|
|
for (; !itDag.isDone(); itDag.next())
|
|
{
|
|
MDagPath curvePath;
|
|
if (itDag.getPath(curvePath) == MS::kSuccess)
|
|
{
|
|
MObject curve = curvePath.node();
|
|
if (!util::isIntermediate(curve) && curve.hasFn(MFn::kNurbsCurve))
|
|
{
|
|
MFnNurbsCurve fn(curvePath);
|
|
if (!init)
|
|
{
|
|
degree = fn.degree();
|
|
form = fn.form();
|
|
init = true;
|
|
}
|
|
else
|
|
{
|
|
if (degree != fn.degree() || form != fn.form())
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void AbcWriteJob::setup(double iFrame, MayaTransformWriterPtr iParent)
|
|
{
|
|
MStatus status;
|
|
|
|
// short-circuit if selection flag is on but this node isn't actively
|
|
// selected
|
|
if (mArgs.useSelectionList && !mSList.hasItem(mCurDag))
|
|
return;
|
|
|
|
MObject ob = mCurDag.node();
|
|
|
|
// skip all intermediate nodes (and their children)
|
|
if (util::isIntermediate(ob))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// skip nodes that aren't renderable (and their children)
|
|
if (mArgs.excludeInvisible && !util::isRenderable(ob))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// look for riCurves flag for flattening all curve objects to a curve group
|
|
MFnDependencyNode fnDepNode(ob, &status);
|
|
MPlug riCurvesPlug = fnDepNode.findPlug("riCurves", true, &status);
|
|
bool riCurvesVal = riCurvesPlug.asBool();
|
|
bool writeOutAsGroup = false;
|
|
if (riCurvesVal)
|
|
{
|
|
writeOutAsGroup = checkCurveGrp();
|
|
if (writeOutAsGroup == false)
|
|
{
|
|
MString msg = "Curves have different degrees or close ";
|
|
msg += "states, not writing out as curve group";
|
|
MGlobal::displayWarning(msg);
|
|
}
|
|
}
|
|
if ( status == MS::kSuccess && riCurvesVal && writeOutAsGroup)
|
|
{
|
|
if( !mArgs.writeCurvesGroup )
|
|
{
|
|
return;
|
|
}
|
|
|
|
MayaNurbsCurveWriterPtr nurbsCurve;
|
|
if (iParent == NULL)
|
|
{
|
|
Alembic::Abc::OObject obj = mRoot.getTop();
|
|
nurbsCurve = MayaNurbsCurveWriterPtr(new MayaNurbsCurveWriter(
|
|
mCurDag, obj, mShapeTimeIndex, true, mArgs));
|
|
}
|
|
else
|
|
{
|
|
Alembic::Abc::OObject obj = iParent->getObject();
|
|
nurbsCurve = MayaNurbsCurveWriterPtr(new MayaNurbsCurveWriter(
|
|
mCurDag, obj, mShapeTimeIndex, true, mArgs));
|
|
}
|
|
|
|
if (nurbsCurve->isAnimated() && mShapeTimeIndex != 0)
|
|
{
|
|
mCurveList.push_back(nurbsCurve);
|
|
mStats.mCurveAnimNum++;
|
|
mStats.mCurveAnimCurves += nurbsCurve->getNumCurves();
|
|
mStats.mCurveAnimCVs += nurbsCurve->getNumCVs();
|
|
}
|
|
else
|
|
{
|
|
mStats.mCurveStaticNum++;
|
|
mStats.mCurveStaticCurves += nurbsCurve->getNumCurves();
|
|
mStats.mCurveStaticCVs += nurbsCurve->getNumCVs();
|
|
}
|
|
|
|
AttributesWriterPtr attrs = nurbsCurve->getAttrs();
|
|
if (mShapeTimeIndex != 0 && attrs->isAnimated())
|
|
mShapeAttrList.push_back(attrs);
|
|
}
|
|
else if (ob.hasFn(MFn::kTransform))
|
|
{
|
|
MayaTransformWriterPtr trans;
|
|
|
|
MFnTransform fnTrans(ob, &status);
|
|
if (status != MS::kSuccess)
|
|
{
|
|
MString msg = "Initialize transform node ";
|
|
msg += mCurDag.fullPathName();
|
|
msg += " failed, skipping.";
|
|
MGlobal::displayWarning(msg);
|
|
return;
|
|
}
|
|
|
|
// parented to the root case
|
|
if (iParent == NULL)
|
|
{
|
|
Alembic::Abc::OObject obj = mRoot.getTop();
|
|
trans = MayaTransformWriterPtr(new MayaTransformWriter(
|
|
obj, mCurDag, mTransTimeIndex, mArgs));
|
|
}
|
|
else
|
|
{
|
|
trans = MayaTransformWriterPtr(new MayaTransformWriter(
|
|
*iParent, mCurDag, mTransTimeIndex, mArgs));
|
|
}
|
|
|
|
if (trans->isAnimated() && mTransTimeIndex != 0)
|
|
{
|
|
mTransList.push_back(trans);
|
|
mStats.mTransAnimNum++;
|
|
}
|
|
else
|
|
{
|
|
mStats.mTransStaticNum++;
|
|
}
|
|
|
|
AttributesWriterPtr attrs = trans->getAttrs();
|
|
if (mTransTimeIndex != 0 && attrs->isAnimated())
|
|
mTransAttrList.push_back(attrs);
|
|
|
|
// loop through the children, making sure to push and pop them
|
|
// from the MDagPath
|
|
unsigned int numChild = mCurDag.childCount();
|
|
for (unsigned int i = 0; i < numChild; ++i)
|
|
{
|
|
if (mCurDag.push(mCurDag.child(i)) == MS::kSuccess)
|
|
{
|
|
setup(iFrame, trans);
|
|
mCurDag.pop();
|
|
}
|
|
}
|
|
}
|
|
else if (ob.hasFn(MFn::kLocator))
|
|
{
|
|
if( !mArgs.writeLocators )
|
|
{
|
|
return;
|
|
}
|
|
|
|
MFnDependencyNode fnLocator(ob, & status);
|
|
if (status != MS::kSuccess)
|
|
{
|
|
MString msg = "Initialize locator node ";
|
|
msg += mCurDag.fullPathName();
|
|
msg += " failed, skipping.";
|
|
MGlobal::displayWarning(msg);
|
|
return;
|
|
}
|
|
|
|
if (iParent != NULL)
|
|
{
|
|
Alembic::Abc::OObject obj = iParent->getObject();
|
|
MayaLocatorWriterPtr locator(new MayaLocatorWriter(
|
|
mCurDag, obj, mShapeTimeIndex, mArgs));
|
|
|
|
if (locator->isAnimated() && mShapeTimeIndex != 0)
|
|
{
|
|
mLocatorList.push_back(locator);
|
|
mStats.mLocatorAnimNum++;
|
|
}
|
|
else
|
|
{
|
|
mStats.mLocatorStaticNum++;
|
|
}
|
|
|
|
AttributesWriterPtr attrs = locator->getAttrs();
|
|
if (mShapeTimeIndex != 0 && attrs->isAnimated())
|
|
mShapeAttrList.push_back(attrs);
|
|
}
|
|
else
|
|
{
|
|
MString err = "Can't translate ";
|
|
err += fnLocator.name() + " since it doesn't have a parent.";
|
|
MGlobal::displayError(err);
|
|
}
|
|
}
|
|
else if (ob.hasFn(MFn::kParticle))
|
|
{
|
|
if( !mArgs.writeParticles )
|
|
{
|
|
return;
|
|
}
|
|
|
|
MFnParticleSystem mFnParticle(ob, &status);
|
|
if (status != MS::kSuccess)
|
|
{
|
|
MString msg = "Initialize particle system ";
|
|
msg += mCurDag.fullPathName();
|
|
msg += " failed, skipping.";
|
|
MGlobal::displayWarning(msg);
|
|
return;
|
|
}
|
|
|
|
if (iParent != NULL)
|
|
{
|
|
Alembic::Abc::OObject obj = iParent->getObject();
|
|
MayaPointPrimitiveWriterPtr particle(new MayaPointPrimitiveWriter(
|
|
iFrame, mCurDag, obj, mShapeTimeIndex, mArgs));
|
|
|
|
if (particle->isAnimated() && mShapeTimeIndex != 0)
|
|
{
|
|
mPointList.push_back(particle);
|
|
mStats.mPointAnimNum++;
|
|
mStats.mPointAnimCVs += particle->getNumCVs();
|
|
}
|
|
else
|
|
{
|
|
mStats.mPointStaticNum++;
|
|
mStats.mPointStaticCVs += particle->getNumCVs();
|
|
}
|
|
|
|
AttributesWriterPtr attrs = particle->getAttrs();
|
|
if (mShapeTimeIndex != 0 && attrs->isAnimated())
|
|
mShapeAttrList.push_back(attrs);
|
|
}
|
|
else
|
|
{
|
|
MString err = "Can't translate ";
|
|
err += mFnParticle.name() + " since it doesn't have a parent.";
|
|
MGlobal::displayError(err);
|
|
}
|
|
}
|
|
else if (ob.hasFn(MFn::kMesh))
|
|
{
|
|
if( !mArgs.writeMeshes)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MFnMesh fnMesh(ob, &status);
|
|
if (status != MS::kSuccess)
|
|
{
|
|
MString msg = "Initialize mesh node ";
|
|
msg += mCurDag.fullPathName();
|
|
msg += " failed, skipping.";
|
|
MGlobal::displayWarning(msg);
|
|
return;
|
|
}
|
|
|
|
if (iParent != NULL)
|
|
{
|
|
Alembic::Abc::OObject obj = iParent->getObject();
|
|
MayaMeshWriterPtr mesh(new MayaMeshWriter(mCurDag, obj,
|
|
mShapeTimeIndex, mArgs));
|
|
|
|
if (mesh->isAnimated() && mShapeTimeIndex != 0)
|
|
{
|
|
mMeshList.push_back(mesh);
|
|
if (mesh->isSubD())
|
|
{
|
|
mStats.mSubDAnimNum++;
|
|
mStats.mSubDAnimCVs += mesh->getNumCVs();
|
|
mStats.mSubDAnimFaces += mesh->getNumFaces();
|
|
}
|
|
else
|
|
{
|
|
mStats.mPolyAnimNum++;
|
|
mStats.mPolyAnimCVs += mesh->getNumCVs();
|
|
mStats.mPolyAnimFaces += mesh->getNumFaces();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mesh->isSubD())
|
|
{
|
|
mStats.mSubDStaticNum++;
|
|
mStats.mSubDStaticCVs += mesh->getNumCVs();
|
|
mStats.mSubDStaticFaces += mesh->getNumFaces();
|
|
}
|
|
else
|
|
{
|
|
mStats.mPolyStaticNum++;
|
|
mStats.mPolyStaticCVs += mesh->getNumCVs();
|
|
mStats.mPolyStaticFaces += mesh->getNumFaces();
|
|
}
|
|
}
|
|
|
|
AttributesWriterPtr attrs = mesh->getAttrs();
|
|
if (mShapeTimeIndex != 0 && attrs->isAnimated())
|
|
mShapeAttrList.push_back(attrs);
|
|
|
|
if (mShapeTimeIndex != 0)
|
|
{
|
|
std::vector< MayaFaceSetWriterPtr >::iterator it;
|
|
for (it = mesh->beginFaces(); it != mesh->endFaces(); ++it)
|
|
{
|
|
if ((*it)->getAttrs() && (*it)->getAttrs()->isAnimated())
|
|
{
|
|
mShapeAttrList.push_back((*it)->getAttrs());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MString err = "Can't translate ";
|
|
err += fnMesh.name() + " since it doesn't have a parent.";
|
|
MGlobal::displayError(err);
|
|
}
|
|
}
|
|
else if (ob.hasFn(MFn::kCamera))
|
|
{
|
|
if( !mArgs.writeCameras )
|
|
{
|
|
return;
|
|
}
|
|
|
|
MFnCamera fnCamera(ob, &status);
|
|
if (status != MS::kSuccess)
|
|
{
|
|
MString msg = "Initialize camera node ";
|
|
msg += mCurDag.fullPathName();
|
|
msg += " failed, skipping.";
|
|
MGlobal::displayWarning(msg);
|
|
return;
|
|
}
|
|
|
|
if (iParent != NULL)
|
|
{
|
|
Alembic::Abc::OObject obj = iParent->getObject();
|
|
MayaCameraWriterPtr camera(new MayaCameraWriter(
|
|
mCurDag, obj, mShapeTimeIndex, mArgs));
|
|
|
|
if (camera->isAnimated() && mShapeTimeIndex != 0)
|
|
{
|
|
mCameraList.push_back(camera);
|
|
mStats.mCameraAnimNum++;
|
|
}
|
|
else
|
|
mStats.mCameraStaticNum++;
|
|
|
|
AttributesWriterPtr attrs = camera->getAttrs();
|
|
if (mShapeTimeIndex != 0 && attrs->isAnimated())
|
|
mShapeAttrList.push_back(attrs);
|
|
}
|
|
else
|
|
{
|
|
MString err = "Can't translate ";
|
|
err += fnCamera.name() + " since it doesn't have a parent.";
|
|
MGlobal::displayError(err);
|
|
}
|
|
}
|
|
else if (ob.hasFn(MFn::kNurbsSurface))
|
|
{
|
|
if( !mArgs.writeNurbsSurfaces )
|
|
{
|
|
return;
|
|
}
|
|
|
|
MFnNurbsSurface fnNurbsSurface(ob, &status);
|
|
if (status != MS::kSuccess)
|
|
{
|
|
MString msg = "Initialize nurbs surface ";
|
|
msg += mCurDag.fullPathName();
|
|
msg += " failed, skipping.";
|
|
MGlobal::displayWarning(msg);
|
|
return;
|
|
}
|
|
|
|
if (iParent != NULL)
|
|
{
|
|
Alembic::Abc::OObject obj = iParent->getObject();
|
|
MayaNurbsSurfaceWriterPtr nurbsSurface(new MayaNurbsSurfaceWriter(
|
|
mCurDag, obj, mShapeTimeIndex, mArgs));
|
|
|
|
if (nurbsSurface->isAnimated() && mShapeTimeIndex != 0)
|
|
{
|
|
mNurbsList.push_back(nurbsSurface);
|
|
mStats.mNurbsAnimNum++;
|
|
mStats.mNurbsAnimCVs += nurbsSurface->getNumCVs();
|
|
}
|
|
else
|
|
{
|
|
mStats.mNurbsStaticNum++;
|
|
mStats.mNurbsStaticCVs += nurbsSurface->getNumCVs();
|
|
}
|
|
|
|
AttributesWriterPtr attrs = nurbsSurface->getAttrs();
|
|
if (mShapeTimeIndex != 0 && attrs->isAnimated())
|
|
mShapeAttrList.push_back(attrs);
|
|
}
|
|
else
|
|
{
|
|
MString err = "Can't translate ";
|
|
err += fnNurbsSurface.name() + " since it doesn't have a parent.";
|
|
MGlobal::displayError(err);
|
|
}
|
|
}
|
|
else if (ob.hasFn(MFn::kNurbsCurve))
|
|
{
|
|
if( !mArgs.writeNurbsCurves )
|
|
{
|
|
return;
|
|
}
|
|
|
|
MFnNurbsCurve fnNurbsCurve(ob, &status);
|
|
if (status != MS::kSuccess)
|
|
{
|
|
MString msg = "Initialize curve node ";
|
|
msg += mCurDag.fullPathName();
|
|
msg += " failed, skipping.";
|
|
MGlobal::displayWarning(msg);
|
|
return;
|
|
}
|
|
|
|
if (iParent != NULL)
|
|
{
|
|
Alembic::Abc::OObject obj = iParent->getObject();
|
|
MayaNurbsCurveWriterPtr nurbsCurve(new MayaNurbsCurveWriter(
|
|
mCurDag, obj, mShapeTimeIndex, false, mArgs));
|
|
|
|
if (nurbsCurve->isAnimated() && mShapeTimeIndex != 0)
|
|
{
|
|
mCurveList.push_back(nurbsCurve);
|
|
mStats.mCurveAnimNum++;
|
|
mStats.mCurveAnimCurves++;
|
|
mStats.mCurveAnimCVs += nurbsCurve->getNumCVs();
|
|
}
|
|
else
|
|
{
|
|
mStats.mCurveStaticNum++;
|
|
mStats.mCurveStaticCurves++;
|
|
mStats.mCurveStaticCVs += nurbsCurve->getNumCVs();
|
|
}
|
|
|
|
AttributesWriterPtr attrs = nurbsCurve->getAttrs();
|
|
if (mShapeTimeIndex != 0 && attrs->isAnimated())
|
|
mShapeAttrList.push_back(attrs);
|
|
}
|
|
else
|
|
{
|
|
MString err = "Can't translate ";
|
|
err += fnNurbsCurve.name() + " since it doesn't have a parent.";
|
|
MGlobal::displayError(err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MString warn = mCurDag.fullPathName() + " is an unsupported type of ";
|
|
warn += ob.apiTypeStr();
|
|
MGlobal::displayWarning(warn);
|
|
}
|
|
}
|
|
|
|
|
|
AbcWriteJob::~AbcWriteJob()
|
|
{
|
|
}
|
|
|
|
bool AbcWriteJob::eval(double iFrame)
|
|
{
|
|
if (iFrame == mFirstFrame)
|
|
{
|
|
// check if the shortnames of any two nodes are the same
|
|
// if so, exit here
|
|
hasDuplicates(mArgs.dagPaths, mArgs.stripNamespace);
|
|
|
|
std::string appWriter = "Maya ";
|
|
appWriter += MGlobal::mayaVersion().asChar();
|
|
appWriter += " AbcExport v";
|
|
appWriter += ABCEXPORT_VERSION;
|
|
|
|
std::string userInfo = "Exported from: ";
|
|
userInfo += MFileIO::currentFile().asChar();
|
|
|
|
// these symbols can't be in the meta data
|
|
if (userInfo.find('=') != std::string::npos ||
|
|
userInfo.find(';') != std::string::npos)
|
|
{
|
|
userInfo = "";
|
|
}
|
|
|
|
MTime sec(1.0, MTime::kSeconds);
|
|
double fps(sec.as(MTime::uiUnit()));
|
|
|
|
#ifdef ALEMBIC_WITH_HDF5
|
|
if (mAsOgawa)
|
|
{
|
|
mRoot = CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(),
|
|
mFileName, fps, appWriter, userInfo,
|
|
Alembic::Abc::ErrorHandler::kThrowPolicy);
|
|
}
|
|
else
|
|
{
|
|
mRoot = CreateArchiveWithInfo(Alembic::AbcCoreHDF5::WriteArchive(),
|
|
mFileName, fps, appWriter, userInfo,
|
|
Alembic::Abc::ErrorHandler::kThrowPolicy);
|
|
}
|
|
#else
|
|
// just write it out as Ogawa
|
|
mRoot = CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(),
|
|
mFileName, fps, appWriter, userInfo,
|
|
Alembic::Abc::ErrorHandler::kThrowPolicy);
|
|
#endif
|
|
|
|
mShapeTimeIndex = mRoot.addTimeSampling(*mShapeTime);
|
|
mTransTimeIndex = mRoot.addTimeSampling(*mTransTime);
|
|
|
|
mBoxProp = Alembic::AbcGeom::CreateOArchiveBounds(mRoot,
|
|
mTransTimeIndex);
|
|
|
|
if (!mRoot.valid())
|
|
{
|
|
std::string theError = "Unable to create abc file";
|
|
throw std::runtime_error(theError);
|
|
}
|
|
|
|
mArgs.setFirstAnimShape = (iFrame == *mShapeFrames.begin());
|
|
|
|
util::ShapeSet::const_iterator end = mArgs.dagPaths.end();
|
|
for (util::ShapeSet::const_iterator it = mArgs.dagPaths.begin();
|
|
it != end; ++it)
|
|
{
|
|
mCurDag = *it;
|
|
setup(iFrame * util::spf(), MayaTransformWriterPtr());
|
|
}
|
|
perFrameCallback(iFrame);
|
|
}
|
|
else
|
|
{
|
|
std::set<double>::iterator checkFrame = mShapeFrames.find(iFrame);
|
|
bool foundShapeFrame = false;
|
|
if (checkFrame != mShapeFrames.end())
|
|
{
|
|
assert(mRoot != NULL);
|
|
foundShapeFrame = true;
|
|
mShapeSamples ++;
|
|
double curTime = iFrame * util::spf();
|
|
|
|
std::vector< MayaCameraWriterPtr >::iterator camIt, camEnd;
|
|
camEnd = mCameraList.end();
|
|
for (camIt = mCameraList.begin(); camIt != camEnd; camIt++)
|
|
{
|
|
(*camIt)->write();
|
|
}
|
|
|
|
std::vector< MayaMeshWriterPtr >::iterator meshIt, meshEnd;
|
|
meshEnd = mMeshList.end();
|
|
for (meshIt = mMeshList.begin(); meshIt != meshEnd; meshIt++)
|
|
{
|
|
(*meshIt)->write();
|
|
if ((*meshIt)->isSubD())
|
|
{
|
|
mStats.mSubDAnimCVs += (*meshIt)->getNumCVs();
|
|
}
|
|
else
|
|
{
|
|
mStats.mPolyAnimCVs += (*meshIt)->getNumCVs();
|
|
}
|
|
}
|
|
|
|
std::vector< MayaNurbsCurveWriterPtr >::iterator curveIt, curveEnd;
|
|
curveEnd = mCurveList.end();
|
|
for (curveIt = mCurveList.begin(); curveIt != curveEnd; curveIt++)
|
|
{
|
|
(*curveIt)->write();
|
|
mStats.mCurveAnimCVs += (*curveIt)->getNumCVs();
|
|
}
|
|
|
|
std::vector< MayaNurbsSurfaceWriterPtr >::iterator nurbsIt,nurbsEnd;
|
|
nurbsEnd = mNurbsList.end();
|
|
for (nurbsIt = mNurbsList.begin(); nurbsIt != nurbsEnd; nurbsIt++)
|
|
{
|
|
(*nurbsIt)->write();
|
|
mStats.mNurbsAnimCVs += (*nurbsIt)->getNumCVs();
|
|
}
|
|
|
|
std::vector< MayaLocatorWriterPtr >::iterator locIt, locEnd;
|
|
locEnd = mLocatorList.end();
|
|
for (locIt = mLocatorList.begin(); locIt != locEnd; locIt++)
|
|
{
|
|
(*locIt)->write();
|
|
}
|
|
|
|
std::vector< MayaPointPrimitiveWriterPtr >::iterator ptIt, ptEnd;
|
|
ptEnd = mPointList.end();
|
|
for (ptIt = mPointList.begin(); ptIt != ptEnd; ptIt++)
|
|
{
|
|
(*ptIt)->write(curTime);
|
|
mStats.mPointAnimCVs += (*ptIt)->getNumCVs();
|
|
}
|
|
|
|
std::vector< AttributesWriterPtr >::iterator sattrCur =
|
|
mShapeAttrList.begin();
|
|
|
|
std::vector< AttributesWriterPtr >::iterator sattrEnd =
|
|
mShapeAttrList.end();
|
|
|
|
for(; sattrCur != sattrEnd; sattrCur++)
|
|
{
|
|
(*sattrCur)->write();
|
|
}
|
|
}
|
|
|
|
checkFrame = mTransFrames.find(iFrame);
|
|
bool foundTransFrame = false;
|
|
if (checkFrame != mTransFrames.end())
|
|
{
|
|
assert(mRoot.valid());
|
|
foundTransFrame = true;
|
|
mTransSamples ++;
|
|
std::vector< MayaTransformWriterPtr >::iterator tcur =
|
|
mTransList.begin();
|
|
|
|
std::vector< MayaTransformWriterPtr >::iterator tend =
|
|
mTransList.end();
|
|
|
|
for (; tcur != tend; tcur++)
|
|
{
|
|
(*tcur)->write();
|
|
}
|
|
|
|
std::vector< AttributesWriterPtr >::iterator tattrCur =
|
|
mTransAttrList.begin();
|
|
|
|
std::vector< AttributesWriterPtr >::iterator tattrEnd =
|
|
mTransAttrList.end();
|
|
|
|
for(; tattrCur != tattrEnd; tattrCur++)
|
|
{
|
|
(*tattrCur)->write();
|
|
}
|
|
}
|
|
|
|
if (foundTransFrame || foundShapeFrame)
|
|
perFrameCallback(iFrame);
|
|
}
|
|
|
|
if (iFrame == mLastFrame)
|
|
{
|
|
postCallback(iFrame);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void AbcWriteJob::perFrameCallback(double iFrame)
|
|
{
|
|
MBoundingBox bbox;
|
|
|
|
util::ShapeSet::iterator it = mArgs.dagPaths.begin();
|
|
const util::ShapeSet::iterator end = mArgs.dagPaths.end();
|
|
for (; it != end; it ++)
|
|
{
|
|
mCurDag = *it;
|
|
|
|
MMatrix eMInvMat;
|
|
if (mArgs.worldSpace)
|
|
{
|
|
eMInvMat.setToIdentity();
|
|
}
|
|
else
|
|
{
|
|
eMInvMat = mCurDag.exclusiveMatrixInverse();
|
|
}
|
|
|
|
bbox.expand(getBoundingBox(iFrame, eMInvMat));
|
|
}
|
|
|
|
Alembic::Abc::V3d min(bbox.min().x, bbox.min().y, bbox.min().z);
|
|
Alembic::Abc::V3d max(bbox.max().x, bbox.max().y, bbox.max().z);
|
|
Alembic::Abc::Box3d b(min, max);
|
|
mBoxProp.set(b);
|
|
|
|
processCallback(mArgs.melPerFrameCallback, true, iFrame, bbox);
|
|
processCallback(mArgs.pythonPerFrameCallback, false, iFrame, bbox);
|
|
}
|
|
|
|
|
|
// write the frame ranges and statistic string on the root
|
|
// Also call the post callbacks
|
|
void AbcWriteJob::postCallback(double iFrame)
|
|
{
|
|
std::string statsStr = "";
|
|
|
|
addToString(statsStr, "SubDStaticNum", mStats.mSubDStaticNum);
|
|
addToString(statsStr, "SubDAnimNum", mStats.mSubDAnimNum);
|
|
addToString(statsStr, "SubDStaticCVs", mStats.mSubDStaticCVs);
|
|
addToString(statsStr, "SubDAnimCVs", mStats.mSubDAnimCVs);
|
|
addToString(statsStr, "SubDStaticFaces", mStats.mSubDStaticFaces);
|
|
addToString(statsStr, "SubDAnimFaces", mStats.mSubDAnimFaces);
|
|
|
|
addToString(statsStr, "PolyStaticNum", mStats.mPolyStaticNum);
|
|
addToString(statsStr, "PolyAnimNum", mStats.mPolyAnimNum);
|
|
addToString(statsStr, "PolyStaticCVs", mStats.mPolyStaticCVs);
|
|
addToString(statsStr, "PolyAnimCVs", mStats.mPolyAnimCVs);
|
|
addToString(statsStr, "PolyStaticFaces", mStats.mPolyStaticFaces);
|
|
addToString(statsStr, "PolyAnimFaces", mStats.mPolyAnimFaces);
|
|
|
|
addToString(statsStr, "CurveStaticNum", mStats.mCurveStaticNum);
|
|
addToString(statsStr, "CurveStaticCurves", mStats.mCurveStaticCurves);
|
|
addToString(statsStr, "CurveAnimNum", mStats.mCurveAnimNum);
|
|
addToString(statsStr, "CurveAnimCurves", mStats.mCurveAnimCurves);
|
|
addToString(statsStr, "CurveStaticCVs", mStats.mCurveStaticCVs);
|
|
addToString(statsStr, "CurveAnimCVs", mStats.mCurveAnimCVs);
|
|
|
|
addToString(statsStr, "PointStaticNum", mStats.mPointStaticNum);
|
|
addToString(statsStr, "PointAnimNum", mStats.mPointAnimNum);
|
|
addToString(statsStr, "PointStaticCVs", mStats.mPointStaticCVs);
|
|
addToString(statsStr, "PointAnimCVs", mStats.mPointAnimCVs);
|
|
|
|
addToString(statsStr, "NurbsStaticNum", mStats.mNurbsStaticNum);
|
|
addToString(statsStr, "NurbsAnimNum", mStats.mNurbsAnimNum);
|
|
addToString(statsStr, "NurbsStaticCVs", mStats.mNurbsStaticCVs);
|
|
addToString(statsStr, "NurbsAnimCVs", mStats.mNurbsAnimCVs);
|
|
|
|
addToString(statsStr, "TransStaticNum", mStats.mTransStaticNum);
|
|
addToString(statsStr, "TransAnimNum", mStats.mTransAnimNum);
|
|
|
|
addToString(statsStr, "LocatorStaticNum", mStats.mLocatorStaticNum);
|
|
addToString(statsStr, "LocatorAnimNum", mStats.mLocatorAnimNum);
|
|
|
|
addToString(statsStr, "CameraStaticNum", mStats.mCameraStaticNum);
|
|
addToString(statsStr, "CameraAnimNum", mStats.mCameraAnimNum);
|
|
|
|
if (statsStr.length() > 0)
|
|
{
|
|
Alembic::Abc::OStringProperty stats(mRoot.getTop().getProperties(),
|
|
"statistics");
|
|
stats.set(statsStr);
|
|
}
|
|
|
|
if (mTransTimeIndex != 0)
|
|
{
|
|
MString propName;
|
|
propName += static_cast<int>(mTransTimeIndex);
|
|
propName += ".samples";
|
|
Alembic::Abc::OUInt32Property samp(mRoot.getTop().getProperties(),
|
|
propName.asChar());
|
|
samp.set(mTransSamples);
|
|
}
|
|
|
|
if (mShapeTimeIndex != 0 && mShapeTimeIndex != mTransTimeIndex)
|
|
{
|
|
MString propName;
|
|
propName += static_cast<int>(mShapeTimeIndex);
|
|
propName += ".samples";
|
|
Alembic::Abc::OUInt32Property samp(mRoot.getTop().getProperties(),
|
|
propName.asChar());
|
|
samp.set(mShapeSamples);
|
|
}
|
|
|
|
MBoundingBox bbox;
|
|
|
|
if (mArgs.melPostCallback.find("#BOUNDS#") != std::string::npos ||
|
|
mArgs.pythonPostCallback.find("#BOUNDS#") != std::string::npos ||
|
|
mArgs.melPostCallback.find("#BOUNDSARRAY#") != std::string::npos ||
|
|
mArgs.pythonPostCallback.find("#BOUNDSARRAY#") != std::string::npos)
|
|
{
|
|
util::ShapeSet::const_iterator it = mArgs.dagPaths.begin();
|
|
const util::ShapeSet::const_iterator end = mArgs.dagPaths.end();
|
|
for (; it != end; it ++)
|
|
{
|
|
mCurDag = *it;
|
|
|
|
MMatrix eMInvMat;
|
|
if (mArgs.worldSpace)
|
|
{
|
|
eMInvMat.setToIdentity();
|
|
}
|
|
else
|
|
{
|
|
eMInvMat = mCurDag.exclusiveMatrixInverse();
|
|
}
|
|
|
|
bbox.expand(getBoundingBox(iFrame, eMInvMat));
|
|
}
|
|
}
|
|
|
|
processCallback(mArgs.melPostCallback, true, iFrame, bbox);
|
|
processCallback(mArgs.pythonPostCallback, false, iFrame, bbox);
|
|
}
|
|
|
|
|