677 lines
21 KiB
C++
677 lines
21 KiB
C++
//-*****************************************************************************
|
|
//
|
|
// Copyright (c) 2009-2012,
|
|
// 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 "MayaUtility.h"
|
|
|
|
// this struct is used in function "bool util::isAnimated(MObject & object, bool checkParent)"
|
|
struct NodesToCheckStruct
|
|
{
|
|
MObject node;
|
|
bool checkParent;
|
|
};
|
|
|
|
// return seconds per frame
|
|
double util::spf()
|
|
{
|
|
static const MTime sec(1.0, MTime::kSeconds);
|
|
return 1.0 / sec.as(MTime::uiUnit());
|
|
}
|
|
|
|
bool util::isAncestorDescendentRelationship(const MDagPath & path1,
|
|
const MDagPath & path2)
|
|
{
|
|
unsigned int length1 = path1.length();
|
|
unsigned int length2 = path2.length();
|
|
unsigned int diff;
|
|
|
|
if (length1 == length2 && !(path1 == path2))
|
|
return false;
|
|
|
|
MDagPath ancestor, descendent;
|
|
if (length1 > length2)
|
|
{
|
|
ancestor = path2;
|
|
descendent = path1;
|
|
diff = length1 - length2;
|
|
}
|
|
else
|
|
{
|
|
ancestor = path1;
|
|
descendent = path2;
|
|
diff = length2 - length1;
|
|
}
|
|
|
|
descendent.pop(diff);
|
|
|
|
bool ret = (ancestor == descendent);
|
|
|
|
if (ret)
|
|
{
|
|
MString err = path1.fullPathName() + " and ";
|
|
err += path2.fullPathName() + " have parenting relationships";
|
|
MGlobal::displayError(err);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
// returns 0 if static, 1 if sampled, and 2 if a curve
|
|
int util::getSampledType(const MPlug& iPlug)
|
|
{
|
|
MPlugArray conns;
|
|
|
|
iPlug.connectedTo(conns, true, false);
|
|
|
|
// it's possible that only some element of an array plug or
|
|
// some component of a compound plus is connected
|
|
if (conns.length() == 0)
|
|
{
|
|
if (iPlug.isArray())
|
|
{
|
|
unsigned int numConnectedElements = iPlug.numConnectedElements();
|
|
for (unsigned int e = 0; e < numConnectedElements; e++)
|
|
{
|
|
int retVal = getSampledType(iPlug.connectionByPhysicalIndex(e));
|
|
if (retVal > 0)
|
|
return retVal;
|
|
}
|
|
}
|
|
else if (iPlug.isCompound() && iPlug.numConnectedChildren() > 0)
|
|
{
|
|
unsigned int numChildren = iPlug.numChildren();
|
|
for (unsigned int c = 0; c < numChildren; c++)
|
|
{
|
|
int retVal = getSampledType(iPlug.child(c));
|
|
if (retVal > 0)
|
|
return retVal;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
MObject ob;
|
|
MFnDependencyNode nodeFn;
|
|
for (unsigned i = 0; i < conns.length(); i++)
|
|
{
|
|
ob = conns[i].node();
|
|
MFn::Type type = ob.apiType();
|
|
|
|
switch (type)
|
|
{
|
|
case MFn::kAnimCurveTimeToAngular:
|
|
case MFn::kAnimCurveTimeToDistance:
|
|
case MFn::kAnimCurveTimeToTime:
|
|
case MFn::kAnimCurveTimeToUnitless:
|
|
{
|
|
nodeFn.setObject(ob);
|
|
MPlug incoming = nodeFn.findPlug("i", true);
|
|
|
|
// sampled
|
|
if (incoming.isConnected())
|
|
return 1;
|
|
|
|
// curve
|
|
else
|
|
return 2;
|
|
}
|
|
break;
|
|
|
|
case MFn::kMute:
|
|
{
|
|
nodeFn.setObject(ob);
|
|
MPlug mutePlug = nodeFn.findPlug("mute", true);
|
|
|
|
// static
|
|
if (mutePlug.asBool())
|
|
return 0;
|
|
// curve
|
|
else
|
|
return 2;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool util::getRotOrder(MTransformationMatrix::RotationOrder iOrder,
|
|
unsigned int & oXAxis, unsigned int & oYAxis, unsigned int & oZAxis)
|
|
{
|
|
switch (iOrder)
|
|
{
|
|
case MTransformationMatrix::kXYZ:
|
|
{
|
|
oXAxis = 0;
|
|
oYAxis = 1;
|
|
oZAxis = 2;
|
|
}
|
|
break;
|
|
|
|
case MTransformationMatrix::kYZX:
|
|
{
|
|
oXAxis = 1;
|
|
oYAxis = 2;
|
|
oZAxis = 0;
|
|
}
|
|
break;
|
|
|
|
case MTransformationMatrix::kZXY:
|
|
{
|
|
oXAxis = 2;
|
|
oYAxis = 0;
|
|
oZAxis = 1;
|
|
}
|
|
break;
|
|
|
|
case MTransformationMatrix::kXZY:
|
|
{
|
|
oXAxis = 0;
|
|
oYAxis = 2;
|
|
oZAxis = 1;
|
|
}
|
|
break;
|
|
|
|
case MTransformationMatrix::kYXZ:
|
|
{
|
|
oXAxis = 1;
|
|
oYAxis = 0;
|
|
oZAxis = 2;
|
|
}
|
|
break;
|
|
|
|
case MTransformationMatrix::kZYX:
|
|
{
|
|
oXAxis = 2;
|
|
oYAxis = 1;
|
|
oZAxis = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// 0 dont write, 1 write static 0, 2 write anim 0, 3 write anim -1
|
|
int util::getVisibilityType(const MPlug & iPlug)
|
|
{
|
|
int type = getSampledType(iPlug);
|
|
|
|
// static case
|
|
if (type == 0)
|
|
{
|
|
// dont write anything
|
|
if (iPlug.asBool())
|
|
return 0;
|
|
|
|
// write static 0
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
// anim write -1
|
|
if (iPlug.asBool())
|
|
return 3;
|
|
|
|
// write anim 0
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
// does this cover all cases?
|
|
bool util::isAnimated(MObject & object, bool checkParent)
|
|
{
|
|
MStatus stat;
|
|
MItDependencyGraph iter(object, MFn::kInvalid,
|
|
MItDependencyGraph::kUpstream,
|
|
MItDependencyGraph::kDepthFirst,
|
|
MItDependencyGraph::kPlugLevel,
|
|
&stat);
|
|
|
|
if (stat!= MS::kSuccess)
|
|
{
|
|
MGlobal::displayError("Unable to create DG iterator ");
|
|
}
|
|
|
|
// MAnimUtil::isAnimated(node) will search the history of the node
|
|
// for any animation curve nodes. It will return true for those nodes
|
|
// that have animation curve in their history.
|
|
// The average time complexity is O(n^2) where n is the number of history
|
|
// nodes. But we can improve the best case by split the loop into two.
|
|
std::vector<NodesToCheckStruct> nodesToCheckAnimCurve;
|
|
|
|
NodesToCheckStruct nodeStruct;
|
|
for (; !iter.isDone(); iter.next())
|
|
{
|
|
MObject node = iter.currentItem();
|
|
|
|
if (node.hasFn(MFn::kPluginDependNode) ||
|
|
node.hasFn( MFn::kConstraint ) ||
|
|
node.hasFn(MFn::kPointConstraint) ||
|
|
node.hasFn(MFn::kAimConstraint) ||
|
|
node.hasFn(MFn::kOrientConstraint) ||
|
|
node.hasFn(MFn::kScaleConstraint) ||
|
|
node.hasFn(MFn::kGeometryConstraint) ||
|
|
node.hasFn(MFn::kNormalConstraint) ||
|
|
node.hasFn(MFn::kTangentConstraint) ||
|
|
node.hasFn(MFn::kParentConstraint) ||
|
|
node.hasFn(MFn::kPoleVectorConstraint) ||
|
|
node.hasFn(MFn::kParentConstraint) ||
|
|
node.hasFn(MFn::kTime) ||
|
|
node.hasFn(MFn::kJoint) ||
|
|
node.hasFn(MFn::kGeometryFilt) ||
|
|
node.hasFn(MFn::kTweak) ||
|
|
node.hasFn(MFn::kPolyTweak) ||
|
|
node.hasFn(MFn::kSubdTweak) ||
|
|
node.hasFn(MFn::kCluster) ||
|
|
node.hasFn(MFn::kFluid) ||
|
|
node.hasFn(MFn::kPolyBoolOp))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (node.hasFn(MFn::kExpression))
|
|
{
|
|
MFnExpression fn(node, &stat);
|
|
if (stat == MS::kSuccess && fn.isAnimated())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// skip shading nodes
|
|
if (!node.hasFn(MFn::kShadingEngine))
|
|
{
|
|
MPlug plug = iter.thisPlug();
|
|
MFnAttribute attr(plug.attribute(), &stat);
|
|
bool checkNodeParent = false;
|
|
if (stat == MS::kSuccess && attr.isWorldSpace())
|
|
{
|
|
checkNodeParent = true;
|
|
}
|
|
|
|
nodeStruct.node = node;
|
|
nodeStruct.checkParent = checkParent || checkNodeParent;
|
|
nodesToCheckAnimCurve.push_back(nodeStruct);
|
|
}
|
|
else
|
|
{
|
|
// and don't traverse the rest of their subgraph
|
|
iter.prune();
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < nodesToCheckAnimCurve.size(); i++)
|
|
{
|
|
if (MAnimUtil::isAnimated(nodesToCheckAnimCurve[i].node, nodesToCheckAnimCurve[i].checkParent))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool util::isDrivenByFBIK(const MFnIkJoint & iJoint)
|
|
{
|
|
// check joints that are driven by Maya FBIK
|
|
// Maya FBIK has no connection to joints' TRS plugs
|
|
// but TRS of joints are driven by FBIK, they are not static
|
|
// Maya 2012's new HumanIK has connections to joints.
|
|
// FBIK is a special case.
|
|
MStatus status = MS::kSuccess;
|
|
if (iJoint.hikJointName(&status).length() > 0 && status) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool util::isDrivenBySplineIK(const MFnIkJoint & iJoint)
|
|
{
|
|
// spline IK can drive the starting joint's translate channel but
|
|
// it has no connection to the translate plug.
|
|
// we treat the joint as animated in this case.
|
|
// find the ikHandle node.
|
|
MPlug msgPlug = iJoint.findPlug("message", false);
|
|
MPlugArray msgPlugDst;
|
|
msgPlug.connectedTo(msgPlugDst, false, true);
|
|
for (unsigned int i = 0; i < msgPlugDst.length(); i++) {
|
|
MFnDependencyNode ikHandle(msgPlugDst[i].node());
|
|
if (!ikHandle.object().hasFn(MFn::kIkHandle)) continue;
|
|
|
|
// find the ikSolver node.
|
|
MPlug ikSolverPlug = ikHandle.findPlug("ikSolver", true);
|
|
MPlugArray ikSolverDst;
|
|
ikSolverPlug.connectedTo(ikSolverDst, true, false);
|
|
for (unsigned int j = 0; j < ikSolverDst.length(); j++) {
|
|
|
|
// return true if the ikSolver is a spline solver.
|
|
if (ikSolverDst[j].node().hasFn(MFn::kSplineSolver)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool util::isIntermediate(const MObject & object)
|
|
{
|
|
MStatus stat;
|
|
MFnDagNode mFn(object);
|
|
|
|
MPlug plug = mFn.findPlug("intermediateObject", false, &stat);
|
|
if (stat == MS::kSuccess && plug.asBool())
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool util::isRenderable(const MObject & object)
|
|
{
|
|
MStatus stat;
|
|
MFnDagNode mFn(object);
|
|
|
|
// templated turned on? return false
|
|
MPlug plug = mFn.findPlug("template", false, &stat);
|
|
if (stat == MS::kSuccess && plug.asBool())
|
|
return false;
|
|
|
|
// visibility or lodVisibility off? return false
|
|
plug = mFn.findPlug("visibility", false, &stat);
|
|
if (stat == MS::kSuccess && !plug.asBool())
|
|
{
|
|
// the value is off. let's check if it has any in-connection,
|
|
// otherwise, it means it is not animated.
|
|
MPlugArray arrayIn;
|
|
plug.connectedTo(arrayIn, true, false, &stat);
|
|
|
|
if (stat == MS::kSuccess && arrayIn.length() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
plug = mFn.findPlug("lodVisibility", false, &stat);
|
|
if (stat == MS::kSuccess && !plug.asBool())
|
|
{
|
|
MPlugArray arrayIn;
|
|
plug.connectedTo(arrayIn, true, false, &stat);
|
|
|
|
if (stat == MS::kSuccess && arrayIn.length() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// this shape is renderable
|
|
return true;
|
|
}
|
|
|
|
MString util::stripNamespaces(const MString & iNodeName, unsigned int iDepth)
|
|
{
|
|
if (iDepth == 0)
|
|
{
|
|
return iNodeName;
|
|
}
|
|
|
|
MStringArray strArray;
|
|
if (iNodeName.split(':', strArray) == MS::kSuccess)
|
|
{
|
|
unsigned int len = strArray.length();
|
|
|
|
// we want to strip off more namespaces than what we have
|
|
// so we just return the last name
|
|
if (len == 0)
|
|
{
|
|
return iNodeName;
|
|
}
|
|
else if (len <= iDepth + 1)
|
|
{
|
|
return strArray[len-1];
|
|
}
|
|
|
|
MString name;
|
|
for (unsigned int i = iDepth; i < len - 1; ++i)
|
|
{
|
|
name += strArray[i];
|
|
name += ":";
|
|
}
|
|
name += strArray[len-1];
|
|
return name;
|
|
}
|
|
|
|
return iNodeName;
|
|
}
|
|
|
|
MString util::getHelpText()
|
|
{
|
|
MString ret =
|
|
"AbcExport [options]\n"
|
|
"Options:\n"
|
|
"-h / -help Print this message.\n"
|
|
"\n"
|
|
"-prs / -preRollStartFrame double\n"
|
|
"The frame to start scene evaluation at. This is used to set the\n"
|
|
"starting frame for time dependent translations and can be used to evaluate\n"
|
|
"run-up that isn't actually translated.\n"
|
|
"\n"
|
|
"-duf / -dontSkipUnwrittenFrames\n"
|
|
"When evaluating multiple translate jobs, the presence of this flag decides\n"
|
|
"whether to evaluate frames between jobs when there is a gap in their frame\n"
|
|
"ranges.\n"
|
|
"\n"
|
|
"-v / -verbose\n"
|
|
"Prints the current frame that is being evaluated.\n"
|
|
"\n"
|
|
"-j / -jobArg string REQUIRED\n"
|
|
"String which contains flags for writing data to a particular file.\n"
|
|
"Multiple jobArgs can be specified.\n"
|
|
"\n"
|
|
"-jobArg flags:\n"
|
|
"\n"
|
|
"-a / -attr string\n"
|
|
"A specific geometric attribute to write out.\n"
|
|
"This flag may occur more than once.\n"
|
|
"\n"
|
|
"-as / -autoSubd\n"
|
|
"If this flag is present and the mesh has crease edges, crease vertices or holes, \n"
|
|
"the mesh (OPolyMesh) would now be written out as an OSubD and crease info will be stored in the Alembic \n"
|
|
"file. Otherwise, creases info won't be preserved in Alembic file \n"
|
|
"unless a custom Boolean attribute SubDivisionMesh has been added to mesh node and its value is true. \n"
|
|
"\n"
|
|
"-atp / -attrPrefix string (default ABC_)\n"
|
|
"Prefix filter for determining which geometric attributes to write out.\n"
|
|
"This flag may occur more than once.\n"
|
|
"\n"
|
|
"-df / -dataFormat string\n"
|
|
"The data format to use to write the file. Can be either HDF or Ogawa.\n"
|
|
"The default is Ogawa.\n"
|
|
"\n"
|
|
"-ef / -eulerFilter\n"
|
|
"If this flag is present, apply Euler filter while sampling rotations.\n"
|
|
"\n"
|
|
"-f / -file string REQUIRED\n"
|
|
"File location to write the Alembic data.\n"
|
|
"\n"
|
|
"-fr / -frameRange double double\n"
|
|
"The frame range to write.\n"
|
|
"Multiple occurrences of -frameRange are supported within a job. Each\n"
|
|
"-frameRange defines a new frame range. -step or -frs will affect the\n"
|
|
"current frame range only.\n"
|
|
"\n"
|
|
"-frs / -frameRelativeSample double\n"
|
|
"frame relative sample that will be written out along the frame range.\n"
|
|
"This flag may occur more than once.\n"
|
|
"\n"
|
|
"-nn / -noNormals\n"
|
|
"If this flag is present normal data for Alembic poly meshes will not be\n"
|
|
"written.\n"
|
|
"\n"
|
|
"-pr / -preRoll\n"
|
|
"If this flag is present, this frame range will not be sampled.\n"
|
|
"\n"
|
|
"-ro / -renderableOnly\n"
|
|
"If this flag is present non-renderable hierarchy (invisible, or templated)\n"
|
|
"will not be written out.\n"
|
|
"\n"
|
|
"-rt / -root\n"
|
|
"Maya dag path which will be parented to the root of the Alembic file.\n"
|
|
"This flag may occur more than once. If unspecified, it defaults to '|' which\n"
|
|
"means the entire scene will be written out.\n"
|
|
"\n"
|
|
"-s / -step double (default 1.0)\n"
|
|
"The time interval (expressed in frames) at which the frame range is sampled.\n"
|
|
"Additional samples around each frame can be specified with -frs.\n"
|
|
"\n"
|
|
"-sl / -selection\n"
|
|
"If this flag is present, write out all all selected nodes from the active\n"
|
|
"selection list that are descendents of the roots specified with -root.\n"
|
|
"\n"
|
|
"-sn / -stripNamespaces (optional int)\n"
|
|
"If this flag is present all namespaces will be stripped off of the node before\n"
|
|
"being written to Alembic. If an optional int is specified after the flag\n"
|
|
"then that many namespaces will be stripped off of the node name. Be careful\n"
|
|
"that the new stripped name does not collide with other sibling node names.\n\n"
|
|
"Examples: \n"
|
|
"taco:foo:bar would be written as just bar with -sn\n"
|
|
"taco:foo:bar would be written as foo:bar with -sn 1\n"
|
|
"\n"
|
|
"-u / -userAttr string\n"
|
|
"A specific user attribute to write out. This flag may occur more than once.\n"
|
|
"\n"
|
|
"-uatp / -userAttrPrefix string\n"
|
|
"Prefix filter for determining which user attributes to write out.\n"
|
|
"This flag may occur more than once.\n"
|
|
"\n"
|
|
"-uv / -uvWrite\n"
|
|
"If this flag is present, uv data for PolyMesh and SubD shapes will be written to\n"
|
|
"the Alembic file. Only the current uv map is used.\n"
|
|
"\n"
|
|
"-uvo / -uvsOnly\n"
|
|
"If this flag is present, only uv data for PolyMesh and SubD shapes will be written\n"
|
|
"to the Alembic file. Only the current uv map is used.\n"
|
|
"\n"
|
|
"-wcs / -writeColorSets\n"
|
|
"Write all color sets on MFnMeshes as color 3 or color 4 indexed geometry \n"
|
|
"parameters with face varying scope.\n"
|
|
"\n"
|
|
"-wfs / -writeFaceSets\n"
|
|
"Write all Face sets on MFnMeshes.\n"
|
|
"\n"
|
|
"-wfg / -wholeFrameGeo\n"
|
|
"If this flag is present data for geometry will only be written out on whole\n"
|
|
"frames.\n"
|
|
"\n"
|
|
"-ws / -worldSpace\n"
|
|
"If this flag is present, any root nodes will be stored in world space.\n"
|
|
"\n"
|
|
"-wv / -writeVisibility\n"
|
|
"If this flag is present, visibility state will be stored in the Alembic\n"
|
|
"file. Otherwise everything written out is treated as visible.\n"
|
|
"\n"
|
|
"-wuvs / -writeUVSets\n"
|
|
"Write all uv sets on MFnMeshes as vector 2 indexed geometry \n"
|
|
"parameters with face varying scope.\n"
|
|
"\n"
|
|
"-mfc / -melPerFrameCallback string\n"
|
|
"When each frame (and the static frame) is evaluated the string specified is\n"
|
|
"evaluated as a Mel command. See below for special processing rules.\n"
|
|
"\n"
|
|
"-mpc / -melPostJobCallback string\n"
|
|
"When the translation has finished the string specified is evaluated as a Mel\n"
|
|
"command. See below for special processing rules.\n"
|
|
"\n"
|
|
"-pfc / -pythonPerFrameCallback string\n"
|
|
"When each frame (and the static frame) is evaluated the string specified is\n"
|
|
"evaluated as a python command. See below for special processing rules.\n"
|
|
"\n"
|
|
"-ppc / -pythonPostJobCallback string\n"
|
|
"When the translation has finished the string specified is evaluated as a\n"
|
|
"python command. See below for special processing rules.\n"
|
|
"\n"
|
|
"Special callback information:\n"
|
|
"On the callbacks, special tokens are replaced with other data, these tokens\n"
|
|
"and what they are replaced with are as follows:\n"
|
|
"\n"
|
|
"#FRAME# replaced with the frame number being evaluated.\n"
|
|
"#FRAME# is ignored in the post callbacks.\n"
|
|
"\n"
|
|
"#BOUNDS# replaced with a string holding bounding box values in minX minY minZ\n"
|
|
"maxX maxY maxZ space seperated order.\n"
|
|
"\n"
|
|
"#BOUNDSARRAY# replaced with the bounding box values as above, but in\n"
|
|
"array form.\n"
|
|
"In Mel: {minX, minY, minZ, maxX, maxY, maxZ}\n"
|
|
"In Python: [minX, minY, minZ, maxX, maxY, maxZ]\n"
|
|
"\n"
|
|
"Examples:\n"
|
|
"\n"
|
|
"AbcExport -j \"-root |group|foo -root |test|path|bar -file /tmp/test.abc\"\n"
|
|
"Writes out everything at foo and below and bar and below to /tmp/test.abc.\n"
|
|
"foo and bar are siblings parented to the root of the Alembic scene.\n"
|
|
"\n"
|
|
"AbcExport -j \"-frameRange 1 5 -step 0.5 -root |group|foo -file /tmp/test.abc\"\n"
|
|
"Writes out everything at foo and below to /tmp/test.abc sampling at frames:\n"
|
|
"1 1.5 2 2.5 3 3.5 4 4.5 5\n"
|
|
"\n"
|
|
"AbcExport -j \"-fr 0 10 -frs -0.1 -frs 0.2 -step 5 -file /tmp/test.abc\"\n"
|
|
"Writes out everything in the scene to /tmp/test.abc sampling at frames:\n"
|
|
"-0.1 0.2 4.9 5.2 9.9 10.2\n"
|
|
"\n"
|
|
"Note: The difference between your highest and lowest frameRelativeSample can\n"
|
|
"not be greater than your step size.\n"
|
|
"\n"
|
|
"AbcExport -j \"-step 0.25 -frs 0.3 -frs 0.60 -fr 1 5 -root foo -file test.abc\"\n"
|
|
"\n"
|
|
"Is illegal because the highest and lowest frameRelativeSamples are 0.3 frames\n"
|
|
"apart.\n"
|
|
"\n"
|
|
"AbcExport -j \"-sl -root |group|foo -file /tmp/test.abc\"\n"
|
|
"Writes out all selected nodes and it's ancestor nodes including up to foo.\n"
|
|
"foo will be parented to the root of the Alembic scene.\n"
|
|
"\n";
|
|
|
|
return ret;
|
|
}
|