Ejemplo n.º 1
0
SampleInfo getSampleInfo
(
   double iFrame,
   Alembic::AbcCoreAbstract::TimeSamplingPtr iTime,
   size_t numSamps
)
{
   ESS_PROFILE_SCOPE("getSampleInfo");
   SampleInfo result;
   if (numSamps == 0)
      numSamps = 1;

   std::pair<Alembic::AbcCoreAbstract::index_t, double> floorIndex = iTime->getFloorIndex(iFrame, numSamps);

   result.floorIndex = floorIndex.first;
   result.ceilIndex = result.floorIndex;

   if (fabs(iFrame - floorIndex.second) < 0.0001) {
      result.alpha = 0.0f;
      return result;
   }

   std::pair<Alembic::AbcCoreAbstract::index_t, double> ceilIndex =
   iTime->getCeilIndex(iFrame, numSamps);

   if (fabs(iFrame - ceilIndex.second) < 0.0001) {
      result.floorIndex = ceilIndex.first;
      result.ceilIndex = result.floorIndex;
      result.alpha = 0.0f;
      return result;
   }

   if (result.floorIndex == ceilIndex.first) {
      result.alpha = 0.0f;
      return result;
   }

   result.ceilIndex = ceilIndex.first;

   result.alpha = (iFrame - floorIndex.second) / (ceilIndex.second - floorIndex.second);
   return result;
}
Ejemplo n.º 2
0
double getWeightAndIndex(double iFrame,
    Alembic::AbcCoreAbstract::TimeSamplingPtr iTime, size_t numSamps,
    Alembic::AbcCoreAbstract::index_t & oIndex,
    Alembic::AbcCoreAbstract::index_t & oCeilIndex)
{
    if (numSamps == 0)
        numSamps = 1;

    std::pair<Alembic::AbcCoreAbstract::index_t, double> floorIndex =
        iTime->getFloorIndex(iFrame, numSamps);

    oIndex = floorIndex.first;
    oCeilIndex = oIndex;

    if (fabs(iFrame - floorIndex.second) < 0.0001)
        return 0.0;

    std::pair<Alembic::AbcCoreAbstract::index_t, double> ceilIndex =
        iTime->getCeilIndex(iFrame, numSamps);

    if (oIndex == ceilIndex.first)
        return 0.0;

    oCeilIndex = ceilIndex.first;

    double alpha = (iFrame - floorIndex.second) /
        (ceilIndex.second - floorIndex.second);

    // we so closely match the ceiling so we'll just use it
    if (fabs(1.0 - alpha) < 0.0001)
    {
        oIndex = oCeilIndex;
        return 0.0;
    }

    return alpha;
}
Ejemplo n.º 3
0
//-*****************************************************************************
void  printTimeSampling( AbcA::TimeSamplingPtr iTime, index_t iMaxSample,
                         double fps )
{
    AbcA::TimeSamplingType timeType = iTime->getTimeSamplingType();
    if ( timeType.isUniform() ) {
        std::cout  << "Uniform Sampling. Start time: " <<
            iTime->getStoredTimes()[0] * fps << " Time per cycle: " <<
            timeType.getTimePerCycle() * fps << std::endl;
    }
    else if ( timeType.isCyclic() ) {
        std::cout << "Cyclic Sampling. Time per cycle:" <<
            timeType.getTimePerCycle() * fps << std::endl;

        const std::vector < double > & storedTimes = iTime->getStoredTimes();
        std::size_t numTimes = iTime->getNumStoredTimes();
        std::cout << "Start cycle times: ";
        for (std::size_t i = 0; i < numTimes; ++i ) {
            if (i != 0) {
                std::cout << ", ";
            }
            std::cout << storedTimes[i] * fps;
        }
        std:: cout << std::endl;
    }
    else {
        std::cout << "Acyclic Sampling." << std::endl;
        const std::vector < double > & storedTimes = iTime->getStoredTimes();
        std::size_t numTimes = iTime->getNumStoredTimes();

        for (std::size_t i = 0; i < numTimes; ++i ) {
            if (i != 0) {
                std::cout << ", ";
            }
            std::cout << storedTimes[i] * fps;
        }
        std:: cout << std::endl;
    }

    std::cout << "Max Num Samples: " << iMaxSample << std::endl;
}
Ejemplo n.º 4
0
MStatus AbcBullet::doIt(const MArgList & args)
{
try
{
	MStatus status;

	MTime oldCurTime = MAnimControl::currentTime();

	MArgParser argData(syntax(), args, &status);

	if (argData.isFlagSet("help"))
	{
		MGlobal::displayInfo(util::getHelpText());
		return MS::kSuccess;
	}

	bool verbose = argData.isFlagSet("verbose");

	// If skipFrame is true, when going through the playback range of the
	// scene, as much frames are skipped when possible.  This could cause
	// a problem for, time dependent solutions like
	// particle system / hair simulation
	bool skipFrame = true;
	if (argData.isFlagSet("dontSkipUnwrittenFrames"))
		skipFrame = false;

	double startEvaluationTime = DBL_MAX;
	if (argData.isFlagSet("preRollStartFrame"))
	{
		double startAt = 0.0;
		argData.getFlagArgument("preRollStartFrame", 0, startAt);
		startEvaluationTime = startAt;
	}

	unsigned int jobSize = argData.numberOfFlagUses("jobArg");

	if (jobSize == 0)
		return status;

	// the frame range we will be iterating over for all jobs,
	// includes frames which are not skipped and the startAt offset
	std::set<double> allFrameRange;

	// this will eventually hold only the animated jobs.
	// its a list because we will be removing jobs from it
	std::list < AbcWriteJobPtr > jobList;

	for (unsigned int jobIndex = 0; jobIndex < jobSize; jobIndex++)
	{
		JobArgs jobArgs;
		MArgList jobArgList;
		argData.getFlagArgumentList("jobArg", jobIndex, jobArgList);
		MString jobArgsStr = jobArgList.asString(0);
		MStringArray jobArgsArray;

		jobArgs.verbose = verbose;
		{
			// parse the job arguments
			// e.g. -perFrameCallbackMel "print \"something\"" will be splitted to
			//	[0] -perFrameCallbackMel
			//	[1] print "something"
			enum State {
				kArgument,			   // parsing an argument (not quoted)
				kDoubleQuotedString,	 // parsing a double quoted string
				kSingleQuotedString,	 // parsing a single quoted string
			};

			State state = kArgument;
			MString stringBuffer;
			for (unsigned int charIdx = 0; charIdx < jobArgsStr.numChars();
				charIdx++)
			{
				MString ch = jobArgsStr.substringW(charIdx, charIdx);
				switch (state)
				{
				case kArgument:
					if (ch == " ")
					{
						// space terminates the current argument
						if (stringBuffer.length() > 0) {
							jobArgsArray.append(stringBuffer);
							stringBuffer.clear();
						}
						// goto another argument
						state = kArgument;
					}
					else if (ch == "\"")
					{
						if (stringBuffer.length() > 0)
						{
							// double quote is part of the argument
							stringBuffer += ch;
						}
						else
						{
							// goto double quoted string
							state = kDoubleQuotedString;
						}
					}
					else if (ch == "'")
					{
						if (stringBuffer.length() > 0)
						{
							// single quote is part of the argument
							stringBuffer += ch;
						}
						else
						{
							// goto single quoted string
							state = kSingleQuotedString;
						}
					}
					else
					{
						stringBuffer += ch;
					}
				break;

				case kDoubleQuotedString:
					// double quote terminates the current string
					if (ch == "\"")
					{
						jobArgsArray.append(stringBuffer);
						stringBuffer.clear();
						state = kArgument;
					}
					else if (ch == "\\")
					{
						// escaped character
						MString nextCh = (++charIdx < jobArgsStr.numChars())
							? jobArgsStr.substringW(charIdx, charIdx) : "\\";
						if (nextCh == "n")	   stringBuffer += "\n";
						else if (nextCh == "t")  stringBuffer += "\t";
						else if (nextCh == "r")  stringBuffer += "\r";
						else if (nextCh == "\\") stringBuffer += "\\";
						else if (nextCh == "'")  stringBuffer += "'";
						else if (nextCh == "\"") stringBuffer += "\"";
						else					 stringBuffer += nextCh;
					}
					else
					{
						stringBuffer += ch;
					}
				break;

				case kSingleQuotedString:
					// single quote terminates the current string
					if (ch == "'")
					{
						jobArgsArray.append(stringBuffer);
						stringBuffer.clear();
						state = kArgument;
					}
					else if (ch == "\\")
					{
						// escaped character
						MString nextCh = (++charIdx < jobArgsStr.numChars())
							? jobArgsStr.substringW(charIdx, charIdx) : "\\";
						if (nextCh == "n")	   stringBuffer += "\n";
						else if (nextCh == "t")  stringBuffer += "\t";
						else if (nextCh == "r")  stringBuffer += "\r";
						else if (nextCh == "\\") stringBuffer += "\\";
						else if (nextCh == "'")  stringBuffer += "'";
						else if (nextCh == "\"") stringBuffer += "\"";
						else					 stringBuffer += nextCh;
					}
					else
					{
						stringBuffer += ch;
					}
				break;
				}
			}

			// the rest of the argument
			if (stringBuffer.length() > 0)
			{
				jobArgsArray.append(stringBuffer);
			}
		}

		double startTime = oldCurTime.value();
		double endTime = oldCurTime.value();
		double strideTime = 1.0;
		bool hasRange = false;
		bool hasRoot = false;
		std::set <double> shutterSamples;
		std::string fileName;

		unsigned int numJobArgs = jobArgsArray.length();
		for (unsigned int i = 0; i < numJobArgs; ++i)
		{
			MString arg = jobArgsArray[i];
			arg.toLowerCase();

			if (arg == "-f" || arg == "-file")
			{
				if (i+1 >= numJobArgs)
				{
					MString err = MStringResource::getString( kInvalidArgFile, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}
				fileName = jobArgsArray[++i].asChar();
			} 

			else if (arg == "-fr" || arg == "-framerange")
			{
				if (i+2 >= numJobArgs || !jobArgsArray[i+1].isDouble() ||
					!jobArgsArray[i+2].isDouble())
				{
					MString err = MStringResource::getString( kInvalidArgFrameRange, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}

				hasRange = true;
				startTime = jobArgsArray[++i].asDouble();
				endTime = jobArgsArray[++i].asDouble();

				// make sure start frame is smaller or equal to endTime
				if (startTime > endTime)
				{
					double temp = startTime;
					startTime = endTime;
					endTime = temp;
				}
			}

			else if (arg == "-frs" || arg == "-framerelativesample")
			{
				if (i+1 >= numJobArgs || !jobArgsArray[i+1].isDouble())
				{
					MString err = MStringResource::getString( kInvalidArgFrameRelativeSample, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}
				shutterSamples.insert(jobArgsArray[++i].asDouble());
			}

			else if (arg == "-nn" || arg == "-nonormals")
			{
				jobArgs.noNormals = true;
			}

			else if (arg == "-ro" || arg == "-renderableonly")
			{
				jobArgs.excludeInvisible = true;
			}

			else if (arg == "-s" || arg == "-step")
			{
				if (i+1 >= numJobArgs || !jobArgsArray[i+1].isDouble())
				{
					MString err = MStringResource::getString( kInvalidArgStep, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}
				strideTime = jobArgsArray[++i].asDouble();
			}

			else if (arg == "-sl" || arg == "-selection")
			{
				jobArgs.useSelectionList = true;
			}

			else if (arg == "-sn" || arg == "-stripnamespaces")
			{
				if (i+1 >= numJobArgs || !jobArgsArray[i+1].isUnsigned())
				{
					// the strip all namespaces case
					// so we pick a very LARGE number
					jobArgs.stripNamespace = 0xffffffff;
				}
				else
				{
					jobArgs.stripNamespace = jobArgsArray[++i].asUnsigned();
				}
			}

			else if (arg == "-uv" || arg == "-uvwrite")
			{
				jobArgs.writeUVs = true;
			}

			else if (arg == "-wcs" || arg == "-writecolorsets")
			{
				jobArgs.writeColorSets = true;
			}

			else if (arg == "-wfs" || arg == "-writefacesets")
			{
				jobArgs.writeFaceSets = true;
			}

			else if (arg == "-ws" || arg == "-worldspace")
			{
				jobArgs.worldSpace = true;
			}

			else if (arg == "-wv" || arg == "-writevisibility")
			{
				jobArgs.writeVisibility = true;
			}

			else if (arg == "-mfc" || arg == "-melperframecallback")
			{
				if (i+1 >= numJobArgs)
				{
					MGlobal::displayError(
						"melPerFrameCallback incorrectly specified.");
					return MS::kFailure;
				}
				jobArgs.melPerFrameCallback = jobArgsArray[++i].asChar();
			}

			else if (arg == "-pfc" || arg == "-pythonperframecallback")
			{
				if (i+1 >= numJobArgs)
				{
					MString err = MStringResource::getString( kInvalidArgPythonPerframeCallback, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}
				jobArgs.pythonPerFrameCallback = jobArgsArray[++i].asChar();
			}

			else if (arg == "-mpc" || arg == "-melpostjobcallback")
			{
				if (i+1 >= numJobArgs)
				{
					MString err = MStringResource::getString( kInvalidArgMelPostJobCallback, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}
				jobArgs.melPostCallback = jobArgsArray[++i].asChar();
			}

			else if (arg == "-ppc" || arg == "-pythonpostjobcallback")
			{
				if (i+1 >= numJobArgs)
				{
					MString err = MStringResource::getString( kInvalidArgPythonPostJobCallback, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}
				jobArgs.pythonPostCallback = jobArgsArray[++i].asChar();
			}

			// geomArbParams - attribute filtering stuff
			else if (arg == "-atp" || arg == "-attrprefix")
			{
				if (i+1 >= numJobArgs)
				{
					MString err = MStringResource::getString( kInvalidArgAttrPrefix, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}
				jobArgs.prefixFilters.push_back(jobArgsArray[++i].asChar());
			}

			else if (arg == "-a" || arg == "-attr")
			{
				if (i+1 >= numJobArgs)
				{
					MString err = MStringResource::getString( kInvalidArgAttr, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}
				jobArgs.attribs.insert(jobArgsArray[++i].asChar());
			}

			// userProperties - attribute filtering stuff
			else if (arg == "-uatp" || arg == "-userattrprefix")
			{
				if (i+1 >= numJobArgs)
				{
					MString err = MStringResource::getString( kInvalidArgUserAttrPrefix, status );
					MGlobal::displayError(err);
					return MS::kFailure;
				}
				jobArgs.userPrefixFilters.push_back(jobArgsArray[++i].asChar());
			}

			else if (arg == "-u" || arg == "-userattr")
			{
				if (i+1 >= numJobArgs)
				{
					MGlobal::displayError("userAttr incorrectly specified.");
					return MS::kFailure;
				}
				jobArgs.userAttribs.insert(jobArgsArray[++i].asChar());
			}

			else if (arg == "-rt" || arg == "-root")
			{
				if (i+1 >= numJobArgs)
				{
					MGlobal::displayError("root incorrectly specified.");
					return MS::kFailure;
				}
				hasRoot = true;
				MString root = jobArgsArray[++i];

				MSelectionList sel;
				if (sel.add(root) != MS::kSuccess)
				{
					MString warn = root;
					warn += " could not be select, skipping.";
					MGlobal::displayWarning(warn);
					continue;
				}

				unsigned int numRoots = sel.length();
				for (unsigned int j = 0; j < numRoots; ++j)
				{
					MDagPath path;
					if (sel.getDagPath(j, path) != MS::kSuccess)
					{
						MString warn = path.fullPathName();
						warn += " (part of ";
						warn += root;
						warn += " ) not a DAG Node, skipping.";
						MGlobal::displayWarning(warn);
						continue;
					}
					jobArgs.dagPaths.insert(path);
				}
			}
			else if (arg == "-ef" || arg == "-eulerfilter")
			{
				jobArgs.filterEulerRotations = true;
			}
			else
			{
				MString warn = "Ignoring unsupported flag: ";
				warn += jobArgsArray[i];
				MGlobal::displayWarning(warn);
			}
		} //  for i

		if (fileName == "")
		{
			MString error = "-file not specified.";
			MGlobal::displayError(error);
			return MS::kFailure;
		}

		{
			MString fileRule, expandName;
			MString alembicFileRule = "alembicCache";
			MString alembicFilePath = "cache/alembic";

			MString queryFileRuleCmd;
			queryFileRuleCmd.format("workspace -q -fre \"^1s\"",
				alembicFileRule);

			MString queryFolderCmd;
			queryFolderCmd.format("workspace -en `workspace -q -fre \"^1s\"`",
				alembicFileRule);

			// query the file rule for alembic cache
			MGlobal::executeCommand(queryFileRuleCmd, fileRule);
			if (fileRule.length() > 0)
			{
				// we have alembic file rule, query the folder
				MGlobal::executeCommand(queryFolderCmd, expandName);
			}
			else
			{
				// alembic file rule does not exist, create it
				MString addFileRuleCmd;
				addFileRuleCmd.format("workspace -fr \"^1s\" \"^2s\"",
					alembicFileRule, alembicFilePath);
				MGlobal::executeCommand(addFileRuleCmd);

				// save the workspace. maya may discard file rules on exit
				MGlobal::executeCommand("workspace -s");

				// query the folder
				MGlobal::executeCommand(queryFolderCmd, expandName);
			}

			// resolve the expanded file rule
			if (expandName.length() == 0)
			{
				expandName = alembicFilePath;
			}

			// get the path to the alembic file rule
			MFileObject directory;
			directory.setRawFullName(expandName);
			MString directoryName = directory.resolvedFullName();

			// make sure the cache folder exists
			if (!directory.exists())
			{
				// create the cache folder
				MString createFolderCmd;
				createFolderCmd.format("sysFile -md \"^1s\"", directoryName);
				MGlobal::executeCommand(createFolderCmd);
			}

			// resolve the relative path
			MFileObject absoluteFile;
			absoluteFile.setRawFullName(fileName.c_str());
#if MAYA_API_VERSION < 201300
			if (absoluteFile.resolvedFullName() !=
				absoluteFile.expandedFullName())
			{
#else
			if (!MFileObject::isAbsolutePath(fileName.c_str())) {
#endif
				// this is a relative path
				MString absoluteFileName = directoryName + "/" +
					fileName.c_str();
				absoluteFile.setRawFullName(absoluteFileName);
				fileName = absoluteFile.resolvedFullName().asChar();
			}
			else
			{
				fileName = absoluteFile.resolvedFullName().asChar();
			}

			// check the path must exist before writing
			MFileObject absoluteFilePath;
			absoluteFilePath.setRawFullName(absoluteFile.path());
			if (!absoluteFilePath.exists()) {
				MString error;
				error.format("Path ^1s does not exist!", absoluteFilePath.resolvedFullName());
				MGlobal::displayError(error);
				return MS::kFailure;
			}

			// check the file is used by any AlembicNode in the scene
			MItDependencyNodes dgIter(MFn::kPluginDependNode);
			for (; !dgIter.isDone(); dgIter.next()) {
				MFnDependencyNode alembicNode(dgIter.thisNode());
				if (alembicNode.typeName() != "AlembicNode") {
					continue;
				}

				MPlug abcFilePlug = alembicNode.findPlug("abc_File");
				if (abcFilePlug.isNull()) {
					continue;
				}

				MFileObject alembicFile;
				alembicFile.setRawFullName(abcFilePlug.asString());
				if (!alembicFile.exists()) {
					continue;
				}

				if (alembicFile.resolvedFullName() == absoluteFile.resolvedFullName()) {
					MString error = "Can't export to an Alembic file which is in use.";
					MGlobal::displayError(error);
					return MS::kFailure;
				}
			}

			std::ofstream ofs(fileName.c_str());
			if (!ofs.is_open()) {
				MString error = MString("Can't write to file: ") + fileName.c_str();
				MGlobal::displayError(error);
				return MS::kFailure;
			}
			ofs.close();
		}

		if (shutterSamples.empty())
		{
			shutterSamples.insert(0.0);
		}

		if (jobArgs.prefixFilters.empty())
		{
			jobArgs.prefixFilters.push_back("ABC_");
		}

		// the list of frames written into the abc file
		std::set<double> transSamples;
		std::set <double>::const_iterator shutter;
		std::set <double>::const_iterator shutterStart = shutterSamples.begin();
		std::set <double>::const_iterator shutterEnd = shutterSamples.end();
		for (double frame = startTime; frame <= endTime; frame += strideTime)
		{
			for (shutter = shutterStart; shutter != shutterEnd; ++shutter)
			{
				double curFrame = *shutter + frame;
				transSamples.insert(curFrame);
			}
		}

		if (transSamples.empty())
		{
			transSamples.insert(startTime);
		}

		if (jobArgs.dagPaths.size() > 1)
		{
			// check for validity of the DagPath relationships complexity : n^2

			util::ShapeSet::const_iterator m, n;
			util::ShapeSet::const_iterator end = jobArgs.dagPaths.end();
			for (m = jobArgs.dagPaths.begin(); m != end; )
			{
				MDagPath path1 = *m;
				m++;
				for (n = m; n != end; n++)
				{
					MDagPath path2 = *n;
					if (util::isAncestorDescendentRelationship(path1,path2))
					{
						MString errorMsg = path1.fullPathName();
						errorMsg += " and ";
						errorMsg += path2.fullPathName();
						errorMsg += " have an ancestor relationship.";
						MGlobal::displayError(errorMsg);
						return MS::kFailure;
					}
				}  // for n
			}  // for m
		}
		// no root is specified, and we aren't using a selection
		// so we'll try to translate the whole Maya scene by using all
		// children of the world as roots.
		else if (!hasRoot && !jobArgs.useSelectionList)
		{
			MSelectionList sel;
#if MAYA_API_VERSION >= 201100
			sel.add("|*", true);
#else
			// older versions of Maya will not be able to find top level nodes
			// within namespaces
			sel.add("|*");
#endif
			unsigned int numRoots = sel.length();
			for (unsigned int i = 0; i < numRoots; ++i)
			{
				MDagPath path;
				sel.getDagPath(i, path);
				jobArgs.dagPaths.insert(path);
			}
		}
		else if (hasRoot && jobArgs.dagPaths.empty())
		{
			MString errorMsg = "No valid root nodes were specified.";
			MGlobal::displayError(errorMsg);
			return MS::kFailure;
		}
		else if (jobArgs.useSelectionList)
		{
			MSelectionList activeList;
			MGlobal::getActiveSelectionList(activeList);
			if (activeList.length() == 0)
			{
				MString errorMsg =
					"-selection specified but nothing is actively selected.";
				MGlobal::displayError(errorMsg);
				return MS::kFailure;
			}
		}

		AbcA::TimeSamplingPtr transTime;

		std::vector<double> samples;
		for (shutter = shutterStart; shutter != shutterEnd; ++shutter)
		{
			samples.push_back((startTime + *shutter) * util::spf());
		}

		if (hasRange)
		{
			transTime.reset(new AbcA::TimeSampling(AbcA::TimeSamplingType(
				static_cast<Alembic::Util::uint32_t>(samples.size()),
				strideTime * util::spf()), samples));
		}
		else
		{
			transTime.reset(new AbcA::TimeSampling());
		}

		AbcWriteJobPtr job(new AbcWriteJob(fileName.c_str(),
			transSamples, transTime, jobArgs));

	   jobList.push_front(job);

		// make sure we add additional whole frames, if we arent skipping
		// the inbetween ones
		if (!skipFrame && !allFrameRange.empty())
		{
			double localMin = *(transSamples.begin());
			std::set<double>::iterator last = transSamples.end();
			last--;
			double localMax = *last;

			double globalMin = *(allFrameRange.begin());
			last = allFrameRange.end();
			last--;
			double globalMax = *last;

			// if the min of our current frame range is beyond
			// what we know about, pad a few more frames
			if (localMin > globalMax)
			{
				for (double f = globalMax; f < localMin; f++)
				{
					allFrameRange.insert(f);
				}
			}

			// if the max of our current frame range is beyond
			// what we know about, pad a few more frames
			if (localMax < globalMin)
			{
				for (double f = localMax; f < globalMin; f++)
				{
					allFrameRange.insert(f);
				}
			}
		}

		// right now we just copy over the translation samples since
		// they are guaranteed to contain all the geometry samples
		allFrameRange.insert(transSamples.begin(), transSamples.end());
	}

	// add extra evaluation run up, if necessary
	if (startEvaluationTime != DBL_MAX && !allFrameRange.empty())
	{
		double firstFrame = *allFrameRange.begin();
		for (double f = startEvaluationTime; f < firstFrame; ++f)
		{
			allFrameRange.insert(f);
		}
	}

	std::set<double>::iterator it = allFrameRange.begin();
	std::set<double>::iterator itEnd = allFrameRange.end();

	MComputation computation;
	computation.beginComputation();

	// loop through every frame in the list, if a job has that frame in it's
	// list of transform or shape frames, then it will write out data and
	// call the perFrameCallback, if that frame is also the last one it has
	// to work on then it will also call the postCallback.
	// If it doesn't have this frame, then it does nothing

	MTimer timer;

	for (; it != itEnd; it++)
	{
		timer.beginTimer();

		MGlobal::viewFrame(*it);
		std::list< AbcWriteJobPtr >::iterator j = jobList.begin();
		std::list< AbcWriteJobPtr >::iterator jend = jobList.end();
		while (j != jend)
		{
			if (computation.isInterruptRequested())
				return MS::kFailure;

			bool lastFrame = (*j)->eval(*it);

			if (lastFrame)
			{
				j = jobList.erase(j);
			}
			else
				j++;
		}

		timer.endTimer();

		if (verbose)
		{
			double frame = *it;
			MString info,arg1,arg2;
			arg1.set(frame);
			arg2.set(timer.elapsedTime());
			info.format( "processed frame: ^1s in ^2s seconds", arg1, arg2 );
			MGlobal::displayInfo(info);
		}


	}
	computation.endComputation();

	// set the time back
	MGlobal::viewFrame(oldCurTime);

	return MS::kSuccess;
}
catch (Alembic::Util::Exception & e)
{
	MString theError("Alembic Exception encountered: ");
	theError += e.what();
	MGlobal::displayError(theError);
	return MS::kFailure;
}
catch (std::exception & e)
{
	MString theError("std::exception encountered: ");
	theError += e.what();
	MGlobal::displayError(theError);
	return MS::kFailure;
}

}