#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <maya/MIOStream.h>
#include <maya/MFStream.h>
#include "animFileUtils.h"
#include "animImportExportStrings.h"
#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MFnAnimCurve.h>
#include <maya/MAnimCurveClipboard.h>
#include <maya/MAnimCurveClipboardItem.h>
#include <maya/MAnimCurveClipboardItemArray.h>
#if defined (OSMac_)
using namespace std;
#endif
const char *kMmString = "mm";
const char *kCmString = "cm";
const char *kMString = "m";
const char *kKmString = "km";
const char *kInString = "in";
const char *kFtString = "ft";
const char *kYdString = "yd";
const char *kMiString = "mi";
const char *kMmLString = "millimeter";
const char *kCmLString = "centimeter";
const char *kMLString = "meter";
const char *kKmLString = "kilometer";
const char *kInLString = "inch";
const char *kFtLString = "foot";
const char *kYdLString = "yard";
const char *kMiLString = "mile";
const char *kRadString = "rad";
const char *kDegString = "deg";
const char *kMinString = "min";
const char *kSecString = "sec";
const char *kRadLString = "radian";
const char *kDegLString = "degree";
const char *kMinLString = "minute";
const char *kSecLString = "second";
const char *kHourTString = "hour";
const char *kMinTString = "min";
const char *kSecTString = "sec";
const char *kMillisecTString = "millisec";
const char *kGameTString = "game";
const char *kFileTString = "film";
const char *kPalTString = "pal";
const char *kNtscTString = "ntsc";
const char *kShowTString = "show";
const char *kPalFTString = "palf";
const char *kNtscFTString = "ntscf";
const char *kUnitlessString = "unitless";
const char *kUnknownTimeString = "Unknown Time Unit";
const char *kUnknownAngularString = "Unknown Angular Unit";
const char *kUnknownLinearString = "Unknown Linear Unit";
animUnitNames::animUnitNames()
{
}
animUnitNames::~animUnitNames()
{
}
void animUnitNames::setToLongName(const MAngle::Unit& unit, MString& name)
{
switch(unit) {
case MAngle::kDegrees:
name.set(kDegLString);
break;
case MAngle::kRadians:
name.set(kRadLString);
break;
case MAngle::kAngMinutes:
name.set(kMinLString);
break;
case MAngle::kAngSeconds:
name.set(kSecLString);
break;
default:
name.set(kUnknownAngularString);
break;
}
}
void animUnitNames::setToShortName(const MAngle::Unit& unit, MString& name)
{
switch(unit) {
case MAngle::kDegrees:
name.set(kDegString);
break;
case MAngle::kRadians:
name.set(kRadString);
break;
case MAngle::kAngMinutes:
name.set(kMinString);
break;
case MAngle::kAngSeconds:
name.set(kSecString);
break;
default:
name.set(kUnknownAngularString);
break;
}
}
void animUnitNames::setToLongName(const MDistance::Unit& unit, MString& name)
{
switch(unit) {
case MDistance::kInches:
name.set(kInLString);
break;
case MDistance::kFeet:
name.set(kFtLString);
break;
case MDistance::kYards:
name.set(kYdLString);
break;
case MDistance::kMiles:
name.set(kMiLString);
break;
case MDistance::kMillimeters:
name.set(kMmLString);
break;
case MDistance::kCentimeters:
name.set(kCmLString);
break;
case MDistance::kKilometers:
name.set(kKmLString);
break;
case MDistance::kMeters:
name.set(kMLString);
break;
default:
name.set(kUnknownLinearString);
break;
}
}
void animUnitNames::setToShortName(const MDistance::Unit& unit, MString& name)
{
switch(unit) {
case MDistance::kInches:
name.set(kInString);
break;
case MDistance::kFeet:
name.set(kFtString);
break;
case MDistance::kYards:
name.set(kYdString);
break;
case MDistance::kMiles:
name.set(kMiString);
break;
case MDistance::kMillimeters:
name.set(kMmString);
break;
case MDistance::kCentimeters:
name.set(kCmString);
break;
case MDistance::kKilometers:
name.set(kKmString);
break;
case MDistance::kMeters:
name.set(kMString);
break;
default:
name.set(kUnknownLinearString);
break;
}
}
void animUnitNames::setToLongName(const MTime::Unit &unit, MString &name)
{
switch(unit) {
case MTime::kHours:
name.set(kHourTString);
break;
case MTime::kMinutes:
name.set(kMinTString);
break;
case MTime::kSeconds:
name.set(kSecTString);
break;
case MTime::kMilliseconds:
name.set(kMillisecTString);
break;
case MTime::kGames:
name.set(kGameTString);
break;
case MTime::kFilm:
name.set(kFileTString);
break;
case MTime::kPALFrame:
name.set(kPalTString);
break;
case MTime::kNTSCFrame:
name.set(kNtscTString);
break;
case MTime::kShowScan:
name.set(kShowTString);
break;
case MTime::kPALField:
name.set(kPalFTString);
break;
case MTime::kNTSCField:
name.set(kNtscFTString);
break;
default:
name.set(kUnknownTimeString);
break;
}
}
void animUnitNames::setToShortName(const MTime::Unit &unit, MString &name)
{
setToLongName(unit, name);
}
bool animUnitNames::setFromName(const MString &str, MAngle::Unit &unit)
{
bool state = true;
const char *name = str.asChar();
if ((strcmp(name, kDegString) == 0) ||
(strcmp(name, kDegLString) == 0)) {
unit = MAngle::kDegrees;
} else if ( (strcmp(name, kRadString) == 0) ||
(strcmp(name, kRadLString) == 0)) {
unit = MAngle::kRadians;
} else if ( (strcmp(name, kMinString) == 0) ||
(strcmp(name, kMinLString) == 0)) {
unit = MAngle::kAngMinutes;
} else if ( (strcmp(name, kSecString) == 0) ||
(strcmp(name, kSecLString) == 0)) {
unit = MAngle::kAngSeconds;
} else {
unit = MAngle::kInvalid;
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kInvalidAngleUnits, stat);
msg.format(msgFmt, str);
MGlobal::displayError(msg);
state = false;
}
return state;
}
bool animUnitNames::setFromName(const MString &str, MDistance::Unit &unit)
{
bool state = true;
const char *name = str.asChar();
if ((strcmp(name, kInString) == 0) ||
(strcmp(name, kInLString) == 0)) {
unit = MDistance::kInches;
} else if ( (strcmp(name, kFtString) == 0) ||
(strcmp(name, kFtLString) == 0)) {
unit = MDistance::kFeet;
} else if ( (strcmp(name, kYdString) == 0) ||
(strcmp(name, kYdLString) == 0)) {
unit = MDistance::kYards;
} else if ( (strcmp(name, kMiString) == 0) ||
(strcmp(name, kMiLString) == 0)) {
unit = MDistance::kMiles;
} else if ( (strcmp(name, kMmString) == 0) ||
(strcmp(name, kMmLString) == 0)) {
unit = MDistance::kMillimeters;
} else if ( (strcmp(name, kCmString) == 0) ||
(strcmp(name, kCmLString) == 0)) {
unit = MDistance::kCentimeters;
} else if ( (strcmp(name, kKmString) == 0) ||
(strcmp(name, kKmLString) == 0)) {
unit = MDistance::kKilometers;
} else if ( (strcmp(name, kMString) == 0) ||
(strcmp(name, kMLString) == 0)) {
unit = MDistance::kMeters;
} else {
state = false;
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kInvalidLinearUnits, stat);
msg.format(msgFmt, str);
MGlobal::displayError(msg);
unit = MDistance::kInvalid;
}
return state;
}
bool animUnitNames::setFromName(const MString &str, MTime::Unit &unit)
{
bool state = true;
const char *name = str.asChar();
if (strcmp(name, kHourTString) == 0) {
unit = MTime::kHours;
} else if (strcmp(name, kMinTString) == 0) {
unit = MTime::kMinutes;
} else if (strcmp(name, kSecTString) == 0) {
unit = MTime::kSeconds;
} else if (strcmp(name, kMillisecTString) == 0) {
unit = MTime::kMilliseconds;
} else if (strcmp(name, kGameTString) == 0) {
unit = MTime::kGames;
} else if (strcmp(name, kFileTString) == 0) {
unit = MTime::kFilm;
} else if (strcmp(name, kPalTString) == 0) {
unit = MTime::kPALFrame;
} else if (strcmp(name, kNtscTString) == 0) {
unit = MTime::kNTSCFrame;
} else if (strcmp(name, kShowTString) == 0) {
unit = MTime::kShowScan;
} else if (strcmp(name, kPalFTString) == 0) {
unit = MTime::kPALField;
} else if (strcmp(name, kNtscFTString) == 0) {
unit = MTime::kNTSCField;
} else {
unit = MTime::kInvalid;
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kInvalidTimeUnits, stat);
msg.format(msgFmt, str);
MGlobal::displayError(msg);
state = false;
}
return state;
}
const char *kWordTangentGlobal = "global";
const char *kWordTangentFixed = "fixed";
const char *kWordTangentLinear = "linear";
const char *kWordTangentFlat = "flat";
const char *kWordTangentSmooth = "spline";
const char *kWordTangentStep = "step";
const char *kWordTangentSlow = "slow";
const char *kWordTangentFast = "fast";
const char *kWordTangentClamped = "clamped";
const char *kWordTangentPlateau = "plateau";
const char *kWordTangentStepNext = "stepnext";
const char *kWordTangentAuto = "auto";
const char *kWordConstant = "constant";
const char *kWordLinear = "linear";
const char *kWordCycle = "cycle";
const char *kWordCycleRelative = "cycleRelative";
const char *kWordOscillate = "oscillate";
const char *kWordTypeUnknown = "unknown";
const char *kWordTypeLinear = "linear";
const char *kWordTypeAngular = "angular";
const char *kWordTypeTime = "time";
const char *kWordTypeUnitless = "unitless";
const char *kAnim = "anim";
const char *kAnimData = "animData";
const char *kMovData = "movData";
const char *kMayaVersion = "mayaVersion";
const char *kAnimVersion = "animVersion";
const char *kTimeUnit = "timeUnit";
const char *kLinearUnit = "linearUnit";
const char *kAngularUnit = "angularUnit";
const char *kStartTime = "startTime";
const char *kEndTime = "endTime";
const char *kStartUnitless = "startUnitless";
const char *kEndUnitless = "endUnitless";
const char *kAnimVersionString = "1.1";
const double kVersionNonWeightedAndBreakdowns = 1.1;
const char *kTwoSpace = " ";
const char *kInputString = "input";
const char *kOutputString = "output";
const char *kWeightedString = "weighted";
const char *kPreInfinityString = "preInfinity";
const char *kPostInfinityString = "postInfinity";
const char *kInputUnitString = "inputUnit";
const char *kOutputUnitString = "outputUnit";
const char *kTanAngleUnitString = "tangentAngleUnit";
const char *kKeysString = "keys";
const char kSemiColonChar = ';';
const char kSpaceChar = ' ';
const char kTabChar = '\t';
const char kHashChar = '#';
const char kNewLineChar = '\n';
const char kSlashChar = '/';
const char kBraceLeftChar = '{';
const char kBraceRightChar = '}';
const char kDoubleQuoteChar = '"';
animBase::animBase ()
{
resetUnits();
}
animBase::~animBase()
{
}
void animBase::resetUnits()
{
timeUnit = MTime::uiUnit();
linearUnit = MDistance::uiUnit();
angularUnit = MAngle::uiUnit();
}
const char *
animBase::tangentTypeAsWord(MFnAnimCurve::TangentType type)
{
switch (type) {
case MFnAnimCurve::kTangentGlobal:
return (kWordTangentGlobal);
case MFnAnimCurve::kTangentFixed:
return (kWordTangentFixed);
case MFnAnimCurve::kTangentLinear:
return (kWordTangentLinear);
case MFnAnimCurve::kTangentFlat:
return (kWordTangentFlat);
case MFnAnimCurve::kTangentSmooth:
return (kWordTangentSmooth);
case MFnAnimCurve::kTangentStep:
return (kWordTangentStep);
case MFnAnimCurve::kTangentStepNext:
return (kWordTangentStepNext);
case MFnAnimCurve::kTangentSlow:
return (kWordTangentSlow);
case MFnAnimCurve::kTangentFast:
return (kWordTangentFast);
case MFnAnimCurve::kTangentClamped:
return (kWordTangentClamped);
case MFnAnimCurve::kTangentPlateau:
return (kWordTangentPlateau);
case MFnAnimCurve::kTangentAuto:
return (kWordTangentAuto);
default:
break;
}
return (kWordTangentGlobal);
}
MFnAnimCurve::TangentType
animBase::wordAsTangentType (char *type)
{
if (strcmp(type, kWordTangentGlobal) == 0) {
return (MFnAnimCurve::kTangentGlobal);
}
if (strcmp(type, kWordTangentFixed) == 0) {
return (MFnAnimCurve::kTangentFixed);
}
if (strcmp(type, kWordTangentLinear) == 0) {
return (MFnAnimCurve::kTangentLinear);
}
if (strcmp(type, kWordTangentFlat) == 0) {
return (MFnAnimCurve::kTangentFlat);
}
if (strcmp(type, kWordTangentSmooth) == 0) {
return (MFnAnimCurve::kTangentSmooth);
}
if (strcmp(type, kWordTangentStep) == 0) {
return (MFnAnimCurve::kTangentStep);
}
if (strcmp(type, kWordTangentStepNext) == 0) {
return (MFnAnimCurve::kTangentStepNext);
}
if (strcmp(type, kWordTangentSlow) == 0) {
return (MFnAnimCurve::kTangentSlow);
}
if (strcmp(type, kWordTangentFast) == 0) {
return (MFnAnimCurve::kTangentFast);
}
if (strcmp(type, kWordTangentClamped) == 0) {
return (MFnAnimCurve::kTangentClamped);
}
if (strcmp(type, kWordTangentPlateau) == 0) {
return (MFnAnimCurve::kTangentPlateau);
}
if (strcmp(type, kWordTangentAuto) == 0) {
return (MFnAnimCurve::kTangentAuto);
}
return (MFnAnimCurve::kTangentGlobal);
}
const char *
animBase::infinityTypeAsWord(MFnAnimCurve::InfinityType type)
{
switch (type) {
case MFnAnimCurve::kConstant:
return (kWordConstant);
case MFnAnimCurve::kLinear:
return (kWordLinear);
case MFnAnimCurve::kCycle:
return (kWordCycle);
case MFnAnimCurve::kCycleRelative:
return (kWordCycleRelative);
case MFnAnimCurve::kOscillate:
return (kWordOscillate);
default:
break;
}
return (kWordConstant);
}
MFnAnimCurve::InfinityType
animBase::wordAsInfinityType(const char *type)
{
if (strcmp(type, kWordConstant) == 0) {
return(MFnAnimCurve::kConstant);
} else if (strcmp(type, kWordLinear) == 0) {
return (MFnAnimCurve::kLinear);
} else if (strcmp(type, kWordCycle) == 0) {
return (MFnAnimCurve::kCycle);
} else if (strcmp(type, kWordCycleRelative) == 0) {
return (MFnAnimCurve::kCycleRelative);
} else if (strcmp(type, kWordOscillate) == 0) {
return (MFnAnimCurve::kOscillate);
}
return (MFnAnimCurve::kConstant);
}
const char *
animBase::outputTypeAsWord (MFnAnimCurve::AnimCurveType type)
{
switch (type) {
case MFnAnimCurve::kAnimCurveTL:
case MFnAnimCurve::kAnimCurveUL:
return (kWordTypeLinear);
case MFnAnimCurve::kAnimCurveTA:
case MFnAnimCurve::kAnimCurveUA:
return (kWordTypeAngular);
case MFnAnimCurve::kAnimCurveTT:
case MFnAnimCurve::kAnimCurveUT:
return (kWordTypeTime);
case MFnAnimCurve::kAnimCurveTU:
case MFnAnimCurve::kAnimCurveUU:
return (kWordTypeUnitless);
case MFnAnimCurve::kAnimCurveUnknown:
return (kWordTypeUnitless);
}
return (kWordTypeUnknown);
}
animBase::AnimBaseType
animBase::wordAsInputType(const char *input)
{
if (strcmp(input, kWordTypeTime) == 0) {
return animBase::kAnimBaseTime;
} else {
return animBase::kAnimBaseUnitless;
}
}
animBase::AnimBaseType
animBase::wordAsOutputType(const char *output)
{
if (strcmp(output, kWordTypeLinear) == 0) {
return animBase::kAnimBaseLinear;
} else if (strcmp(output, kWordTypeAngular) == 0) {
return animBase::kAnimBaseAngular;
} else if (strcmp(output, kWordTypeTime) == 0) {
return animBase::kAnimBaseTime;
} else {
return animBase::kAnimBaseUnitless;
}
}
const char *
animBase::boolInputTypeAsWord(bool isUnitless)
{
if (isUnitless) {
return (kWordTypeUnitless);
} else {
return (kWordTypeTime);
}
}
MFnAnimCurve::AnimCurveType
animBase::typeAsAnimCurveType( animBase::AnimBaseType input,
animBase::AnimBaseType output)
{
MFnAnimCurve::AnimCurveType type = MFnAnimCurve::kAnimCurveUnknown;
switch (output) {
case kAnimBaseLinear:
if (kAnimBaseUnitless == input) {
type = MFnAnimCurve::kAnimCurveUL;
} else {
type = MFnAnimCurve::kAnimCurveTL;
}
break;
case kAnimBaseAngular:
if (kAnimBaseUnitless == input) {
type = MFnAnimCurve::kAnimCurveUA;
} else {
type = MFnAnimCurve::kAnimCurveTA;
}
break;
case kAnimBaseTime:
if (kAnimBaseUnitless == input) {
type = MFnAnimCurve::kAnimCurveUT;
} else {
type = MFnAnimCurve::kAnimCurveTT;
}
break;
case kAnimBaseUnitless:
if (kAnimBaseUnitless == input) {
type = MFnAnimCurve::kAnimCurveUU;
} else {
type = MFnAnimCurve::kAnimCurveTU;
}
break;
default:
break;
}
return type;
}
double animBase::asDouble (ifstream &clipFile)
{
advance(clipFile);
double value;
clipFile >> value;
return (value);
}
bool animBase::isNextNumeric(ifstream &clipFile)
{
bool numeric = false;
advance(clipFile);
char next = clipFile.peek();
if (next >= '0' && next <= '9') {
numeric = true;
}
return numeric;
}
void animBase::advance (ifstream &clipFile)
{
while (clipFile) {
clipFile >> ws;
char next = clipFile.peek();
if (next == kSemiColonChar) {
clipFile.ignore(1, kSemiColonChar);
continue;
}
if (next == kSlashChar || next == kHashChar) {
clipFile.ignore(INT_MAX, kNewLineChar);
continue;
}
break;
}
}
char* animBase::asWord (ifstream &clipFile, bool includeWS )
{
static const int kBufLength = 1024;
static char string[kBufLength];
advance(clipFile);
char *c = string;
clipFile.read (c, 1);
if (*c == kDoubleQuoteChar) {
clipFile.read(c, 1);
while(!clipFile.eof() && (*c != kDoubleQuoteChar)) {
c++;
if (c-string >= kBufLength) {
break;
}
clipFile.read(c, 1);
}
} else {
if (*c == kBraceLeftChar || *c == kBraceRightChar) {
c++;
} else {
while(!clipFile.eof() && (*c != kSemiColonChar)) {
if (!includeWS && ((*c == kSpaceChar) || (*c == kTabChar))) {
break;
}
c++;
if (c-string >= kBufLength) {
break;
}
clipFile.read(c, 1);
}
}
}
*c = 0x00;
return (string);
}
char animBase::asChar (ifstream &clipFile)
{
advance(clipFile);
return clipFile.get();
}
bool animBase::isEquivalent(double a, double b)
{
const double tolerance = 1.0e-10;
return ((a > b) ? (a - b <= tolerance) : (b - a <= tolerance));
}
animReader::animReader ()
: animVersion (1.0)
, convertAnglesFromV2To3(false)
, convertAnglesFromV3To2(false)
{
}
animReader::~animReader()
{
}
MStatus
animReader::readClipboard(ifstream &readAnim, MAnimCurveClipboard& cb)
{
double startTime = 1.0;
double endTime = 0.0;
double startUnitless = 1.0;
double endUnitless = 0.0;
resetUnits();
convertAnglesFromV2To3 = false;
convertAnglesFromV3To2 = false;
char *dataType = NULL;
bool hasVersionString = false;
while (!readAnim.eof()) {
advance(readAnim);
dataType = asWord(readAnim);
if (strcmp(dataType, kAnimVersion) == 0) {
MString version(asWord(readAnim));
animVersion = version.asDouble();
MString thisVersion(kAnimVersionString);
hasVersionString = true;
if (version != thisVersion) {
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kInvalidVersion,
stat);
msg.format(msgFmt, version, thisVersion);
MGlobal::displayWarning(msg);
}
} else if (strcmp(dataType, kMayaVersion) == 0) {
MString version(asWord(readAnim, true));
MString currentVersion = MGlobal::mayaVersion();
if (currentVersion.substring(0,1) == "2.") {
MString vCheck = version.substring(0, 1);
if (vCheck != "2.") {
convertAnglesFromV3To2 = true;
}
} else {
MString vCheck = version.substring(0, 1);
if (vCheck == "2.") {
convertAnglesFromV2To3 = true;
}
}
} else if (strcmp(dataType, kTimeUnit) == 0) {
MString timeUnitString(asWord(readAnim));
if (!animUnitNames::setFromName(timeUnitString, timeUnit)) {
MString unitName;
timeUnit = MTime::uiUnit();
animUnitNames::setToShortName(timeUnit, unitName);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kSettingToUnit,
stat);
msg.format(msgFmt, kTimeUnit, unitName);
MGlobal::displayWarning(msg);
}
} else if (strcmp(dataType, kLinearUnit) == 0) {
MString linearUnitString(asWord(readAnim));
if (!animUnitNames::setFromName(linearUnitString, linearUnit)) {
MString unitName;
linearUnit = MDistance::uiUnit();
animUnitNames::setToShortName(linearUnit, unitName);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kSettingToUnit,
stat);
msg.format(msgFmt, kLinearUnit, unitName);
MGlobal::displayWarning(msg);
}
} else if (strcmp(dataType, kAngularUnit) == 0) {
MString angularUnitString(asWord(readAnim));
if (!animUnitNames::setFromName(angularUnitString, angularUnit)) {
MString unitName;
angularUnit = MAngle::uiUnit();
animUnitNames::setToShortName(angularUnit, unitName);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kSettingToUnit,
stat);
msg.format(msgFmt, kAngularUnit, unitName);
MGlobal::displayWarning(msg);
}
} else if (strcmp(dataType, kStartTime) == 0) {
startTime = asDouble(readAnim);
} else if (strcmp(dataType, kEndTime) == 0) {
endTime = asDouble(readAnim);
} else if (strcmp(dataType, kStartUnitless) == 0) {
startUnitless = asDouble(readAnim);
} else if (strcmp(dataType, kEndUnitless) == 0) {
endUnitless = asDouble(readAnim);
} else {
break;
}
}
if (!hasVersionString) {
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kMissingKeyword, stat);
msg.format(msgFmt, kAnimVersion);
MGlobal::displayError(msg);
return (MS::kFailure);
}
MDistance::Unit oldDistanceUnit = MDistance::uiUnit();
MTime::Unit oldTimeUnit = MTime::uiUnit();
MDistance::setUIUnit(linearUnit);
MTime::setUIUnit(timeUnit);
MAnimCurveClipboardItemArray clipboardArray;
while (!readAnim.eof()) {
if (NULL == dataType) {
dataType = asWord(readAnim);
}
if (strcmp(dataType, kAnim) == 0) {
MString fullAttributeName, leafAttributeName, nodeName;
if (!isNextNumeric(readAnim)) {
fullAttributeName.set(asWord(readAnim));
if (!isNextNumeric(readAnim)) {
leafAttributeName.set(asWord(readAnim));
nodeName.set(asWord(readAnim));
}
}
unsigned rowCount, childCount, attrCount;
rowCount = (unsigned)asDouble(readAnim);
childCount = (unsigned)asDouble(readAnim);
attrCount = (unsigned)asDouble(readAnim);
dataType = asWord(readAnim);
if (strcmp(dataType, kAnimData) == 0) {
MAnimCurveClipboardItem clipboardItem;
if (readAnimCurve(readAnim, clipboardItem)) {
clipboardItem.setAddressingInfo(rowCount,
childCount, attrCount);
clipboardItem.setNameInfo( nodeName,
fullAttributeName,
leafAttributeName);
clipboardArray.append(clipboardItem);
} else {
MStatus stringStat;
MString msg = MStringResource::getString(kCouldNotReadAnim,
stringStat);
MGlobal::displayError(msg);
}
} else {
MAnimCurveClipboardItem clipboardItem;
clipboardItem.setAddressingInfo(rowCount,
childCount, attrCount);
clipboardItem.setNameInfo( fullAttributeName,
nodeName,
leafAttributeName);
clipboardArray.append(clipboardItem);
continue;
}
} else {
if (!readAnim.eof()) {
MString warnStr(dataType);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kUnknownKeyword,
stat);
msg.format(msgFmt, warnStr);
MGlobal::displayError(msg);
readAnim.ignore(INT_MAX, kNewLineChar);
} else {
break;
}
}
dataType = NULL;
}
if (MS::kSuccess != cb.set( clipboardArray,
MTime(startTime, timeUnit), MTime(endTime, timeUnit),
(float) startUnitless, (float) endUnitless)) {
MStatus stringStat;
MString msg = MStringResource::getString(kClipboardFailure,
stringStat);
MGlobal::displayError(msg);
}
MDistance::setUIUnit(oldDistanceUnit);
MTime::setUIUnit(oldTimeUnit);
return (MS::kSuccess);
}
void animReader::convertAnglesAndWeights3To2(MFnAnimCurve::AnimCurveType type,
bool isWeighted, MAngle &angle, double &weight)
{
double oldAngle = angle.as(MAngle::kRadians);
double newAngle = oldAngle;
double xScale = 1.0;
double yScale = 1.0;
MTime tOne(1.0, MTime::kSeconds);
if (type == MFnAnimCurve::kAnimCurveTT ||
type == MFnAnimCurve::kAnimCurveTL ||
type == MFnAnimCurve::kAnimCurveTA ||
type == MFnAnimCurve::kAnimCurveTU) {
xScale = tOne.as(MTime::uiUnit());
}
switch (type) {
case MFnAnimCurve::kAnimCurveTT:
case MFnAnimCurve::kAnimCurveUT:
yScale = tOne.as(MTime::uiUnit());
break;
case MFnAnimCurve::kAnimCurveTL:
case MFnAnimCurve::kAnimCurveUL:
{
MDistance dOne(1.0, MDistance::internalUnit());
yScale = dOne.as(linearUnit);
}
break;
case MFnAnimCurve::kAnimCurveTA:
case MFnAnimCurve::kAnimCurveUA:
{
MAngle aOne(1.0, MAngle::internalUnit());
yScale = aOne.as(angularUnit);
}
break;
case MFnAnimCurve::kAnimCurveTU:
case MFnAnimCurve::kAnimCurveUU:
default:
break;
}
double tanAngle = tan(oldAngle);
newAngle = atan((xScale*tanAngle)/yScale);
if (isWeighted) {
double sinAngle = sin(oldAngle);
double cosAngle = cos(oldAngle);
double denominator = (yScale*yScale*sinAngle*sinAngle) +
(xScale*xScale*cosAngle*cosAngle);
weight = sqrtl(weight/denominator);
}
MAngle finalAngle(newAngle, MAngle::kRadians);
angle = finalAngle;
}
void animReader::convertAnglesAndWeights2To3(MFnAnimCurve::AnimCurveType type,
bool isWeighted, MAngle &angle, double &weight)
{
double oldAngle = angle.as(MAngle::kRadians);
double newAngle = oldAngle;
double newWeight = weight;
double xScale = 1.0;
double yScale = 1.0;
MTime tOne(1.0, MTime::kSeconds);
if (type == MFnAnimCurve::kAnimCurveTT ||
type == MFnAnimCurve::kAnimCurveTL ||
type == MFnAnimCurve::kAnimCurveTA ||
type == MFnAnimCurve::kAnimCurveTU) {
xScale = tOne.as(MTime::uiUnit());
}
switch (type) {
case MFnAnimCurve::kAnimCurveTT:
case MFnAnimCurve::kAnimCurveUT:
yScale = tOne.as(MTime::uiUnit());
break;
case MFnAnimCurve::kAnimCurveTL:
case MFnAnimCurve::kAnimCurveUL:
{
MDistance dOne(1.0, MDistance::internalUnit());
yScale = dOne.as(linearUnit);
}
break;
case MFnAnimCurve::kAnimCurveTA:
case MFnAnimCurve::kAnimCurveUA:
{
MAngle aOne(1.0, MAngle::internalUnit());
yScale = aOne.as(angularUnit);
}
break;
case MFnAnimCurve::kAnimCurveTU:
case MFnAnimCurve::kAnimCurveUU:
default:
break;
}
const double quarter = M_PI/2;
if (isEquivalent(oldAngle, 0.0) ||
isEquivalent(oldAngle, quarter) ||
isEquivalent(oldAngle, -quarter)) {
newAngle = oldAngle;
if (isWeighted) {
newWeight = yScale*oldAngle;
}
} else {
double tanAngle = tan(oldAngle);
newAngle = atan((yScale*tanAngle)/xScale);
if (isWeighted) {
double cosAngle = cos(oldAngle);
double cosSq = cosAngle*cosAngle;
double wSq = (weight*weight) *
(((xScale*xScale - yScale*yScale)*cosSq) + (yScale*yScale));
newWeight = sqrtl(wSq);
}
}
weight = newWeight;
MAngle finalAngle(newAngle, MAngle::kRadians);
angle = finalAngle;
}
bool animReader::readAnimCurve(
ifstream &clipFile, MAnimCurveClipboardItem &item)
{
MFnAnimCurve animCurve;
MObject animCurveObj;
animBase::AnimBaseType input = wordAsInputType(kWordTypeTime);
animBase::AnimBaseType output = wordAsOutputType(kWordTypeLinear);
MFnAnimCurve::InfinityType preInf = wordAsInfinityType(kWordConstant);
MFnAnimCurve::InfinityType postInf = wordAsInfinityType(kWordConstant);
MString inputUnitName;
animUnitNames::setToShortName(timeUnit, inputUnitName);
MString outputUnitName;
MAngle::Unit tanAngleUnit = angularUnit;
bool isWeighted (false);
char *dataType;
while (!clipFile.eof()) {
advance(clipFile);
dataType = asWord(clipFile);
if (strcmp(dataType, kInputString) == 0) {
input = wordAsInputType(asWord(clipFile));
} else if (strcmp(dataType, kOutputString) == 0) {
output = wordAsOutputType(asWord(clipFile));
} else if (strcmp(dataType, kWeightedString) == 0) {
isWeighted = (asDouble(clipFile) == 1.0);
} else if (strcmp(dataType, kPreInfinityString) == 0) {
preInf = wordAsInfinityType(asWord(clipFile));
} else if (strcmp(dataType, kPostInfinityString) == 0) {
postInf = wordAsInfinityType(asWord(clipFile));
} else if (strcmp(dataType, kInputUnitString) == 0) {
inputUnitName.set(asWord(clipFile));
} else if (strcmp(dataType, kOutputUnitString) == 0) {
outputUnitName.set(asWord(clipFile));
} else if (strcmp(dataType, kTanAngleUnitString) == 0) {
MString tUnit(asWord(clipFile));
if (!animUnitNames::setFromName(tUnit, tanAngleUnit)) {
MString unitName;
tanAngleUnit = angularUnit;
animUnitNames::setToShortName(tanAngleUnit, unitName);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kSettingTanAngleUnit, stat);
msg.format(msgFmt, unitName);
MGlobal::displayError(msg);
}
} else if (strcmp(dataType, kKeysString) == 0) {
clipFile.ignore(INT_MAX, kNewLineChar);
break;
} else if (strcmp(dataType, "{") == 0) {
continue;
} else {
MString warnStr(dataType);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kUnknownKeyword,
stat);
msg.format(msgFmt, warnStr);
MGlobal::displayError(msg);
continue;
}
}
MStatus status;
MFnAnimCurve::AnimCurveType type = typeAsAnimCurveType(input, output);
animCurveObj = animCurve.create(type, NULL, &status);
if (status != MS::kSuccess) {
MString msg = MStringResource::getString(kCouldNotCreateAnim, status);
MGlobal::displayError(msg);
return false;
}
animCurve.setIsWeighted(isWeighted);
animCurve.setPreInfinityType(preInf);
animCurve.setPostInfinityType(postInf);
MTime::Unit inputTimeUnit;
if (input == kAnimBaseTime) {
if (!animUnitNames::setFromName(inputUnitName, inputTimeUnit)) {
MString unitName;
inputTimeUnit = timeUnit;
animUnitNames::setToShortName(inputTimeUnit, unitName);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kSettingToUnit,
stat);
msg.format(msgFmt, kInputUnitString, unitName);
MGlobal::displayWarning(msg);
}
}
MTime::Unit outputTimeUnit;
if (output == kAnimBaseTime) {
if (!animUnitNames::setFromName(outputUnitName, outputTimeUnit)) {
MString unitName;
outputTimeUnit = timeUnit;
animUnitNames::setToShortName(outputTimeUnit, unitName);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kSettingToUnit,
stat);
msg.format(msgFmt, kOutputUnitString, unitName);
MGlobal::displayWarning(msg);
}
}
int index = 0;
double conversion = 1.0;
if (output == kAnimBaseLinear) {
MDistance::Unit unit;
if (outputUnitName.length() != 0) {
if (!animUnitNames::setFromName(outputUnitName, unit)) {
MString unitName;
unit = linearUnit;
animUnitNames::setToShortName(unit, unitName);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kSettingToUnit,
stat);
msg.format(msgFmt, kOutputUnitString, unitName);
MGlobal::displayWarning(msg);
}
} else {
unit = linearUnit;
}
if (unit != MDistance::kCentimeters) {
MDistance one(1.0, unit);
conversion = one.as(MDistance::kCentimeters);
}
} else if (output == kAnimBaseAngular) {
MAngle::Unit unit;
if (outputUnitName.length() != 0) {
if (!animUnitNames::setFromName(outputUnitName, unit)) {
MString unitName;
unit = angularUnit;
animUnitNames::setToShortName(unit, unitName);
MStatus stat;
MString msg;
MString msgFmt = MStringResource::getString(kSettingToUnit,
stat);
msg.format(msgFmt, kOutputUnitString, unitName);
MGlobal::displayWarning(msg);
}
} else {
unit = angularUnit;
}
if (unit != MAngle::kRadians) {
MAngle one(1.0, unit);
conversion = one.as(MAngle::kRadians);
}
}
advance(clipFile);
char c = clipFile.peek();
while (clipFile && c != kBraceRightChar) {
double t = asDouble (clipFile);
double val = asDouble (clipFile);
MFnAnimCurve::TangentType tanIn = wordAsTangentType(asWord(clipFile));
MFnAnimCurve::TangentType tanOut = wordAsTangentType(asWord(clipFile));
switch (type) {
case MFnAnimCurve::kAnimCurveTT:
index = animCurve.addKey( MTime(val, inputTimeUnit),
MTime(val, outputTimeUnit),
tanIn, tanOut, NULL, &status);
break;
case MFnAnimCurve::kAnimCurveTL:
case MFnAnimCurve::kAnimCurveTA:
case MFnAnimCurve::kAnimCurveTU:
index = animCurve.addKey( MTime(t, inputTimeUnit),
val*conversion, tanIn, tanOut,
NULL, &status);
break;
case MFnAnimCurve::kAnimCurveUL:
case MFnAnimCurve::kAnimCurveUA:
case MFnAnimCurve::kAnimCurveUU:
index = animCurve.addKey( t, val*conversion,
tanIn, tanOut,
NULL, &status);
break;
case MFnAnimCurve::kAnimCurveUT:
index = animCurve.addKey( t, MTime(val, outputTimeUnit),
tanIn, tanOut,
NULL, &status);
break;
default:
MString msg = MStringResource::getString(kUnknownNode, status);
MGlobal::displayError(msg);
return false;
}
if (status != MS::kSuccess) {
MStatus stringStat;
MString msg = MStringResource::getString(kCouldNotKey, stringStat);
MGlobal::displayError(msg);
}
bool tLocked = bool(asDouble(clipFile) == 1.0);
bool swLocked = bool(asDouble(clipFile) == 1.0);
bool isBreakdown (false);
if (animVersion >= kVersionNonWeightedAndBreakdowns) {
isBreakdown = (asDouble(clipFile) == 1.0);
}
if (tanIn == MFnAnimCurve::kTangentFixed) {
MAngle inAngle(asDouble(clipFile), tanAngleUnit);
double inWeight = asDouble(clipFile);
if (convertAnglesFromV2To3) {
convertAnglesAndWeights2To3(type,isWeighted,inAngle,inWeight);
} else if (convertAnglesFromV3To2) {
convertAnglesAndWeights3To2(type,isWeighted,inAngle,inWeight);
}
animCurve.setTangentsLocked(index, false);
animCurve.setTangent(index, inAngle, inWeight, true);
}
if (tanOut == MFnAnimCurve::kTangentFixed) {
MAngle outAngle(asDouble(clipFile), tanAngleUnit);
double outWeight = asDouble(clipFile);
if (convertAnglesFromV2To3) {
convertAnglesAndWeights2To3(type,isWeighted,outAngle,outWeight);
} else if (convertAnglesFromV3To2) {
convertAnglesAndWeights3To2(type,isWeighted,outAngle,outWeight);
}
animCurve.setTangentsLocked(index, false);
animCurve.setTangent(index, outAngle, outWeight, false);
}
animCurve.setWeightsLocked(index, swLocked);
animCurve.setTangentsLocked(index, tLocked);
animCurve.setIsBreakdown (index, isBreakdown);
clipFile.ignore(INT_MAX, kNewLineChar);
advance(clipFile);
c = clipFile.peek();
}
if (c == kBraceRightChar) {
clipFile.ignore(INT_MAX, kNewLineChar);
}
advance(clipFile);
if (clipFile.peek() == kBraceRightChar) {
clipFile.ignore(INT_MAX, kNewLineChar);
} else {
MStatus stringStat;
MString msg = MStringResource::getString(kMissingBrace, stringStat);
MGlobal::displayError(msg);
}
if (!animCurveObj.isNull()) {
item.setAnimCurve(animCurveObj);
}
MGlobal::deleteNode(animCurveObj);
return true;
}
animWriter::animWriter()
{
}
animWriter::~animWriter()
{
}
MStatus
animWriter::writeClipboard( ofstream& animFile,
const MAnimCurveClipboard &cb,
bool nodeNames ,
bool verboseUnits )
{
MStatus status = MS::kFailure;
if (cb.isEmpty()) {
return status;
}
resetUnits();
if (!writeHeader(animFile)) {
return (MS::kFailure);
}
const MAnimCurveClipboardItemArray &clipboardArray =
cb.clipboardItems(&status);
if (MS::kSuccess != status) {
return status;
}
for (unsigned int i = 0; i < clipboardArray.length(); i++) {
const MAnimCurveClipboardItem &clipboardItem = clipboardArray[i];
MStatus statusInLoop = MS::kSuccess;
const MObject animCurveObj = clipboardItem.animCurve(&statusInLoop);
bool placeHolder = false;
if (MS::kSuccess != statusInLoop || animCurveObj.isNull()) {
placeHolder = true;
}
if (!writeAnim(animFile, clipboardItem, placeHolder, nodeNames)) {
return (MS::kFailure);
}
if (placeHolder) {
continue;
}
if (!writeAnimCurve(animFile, &animCurveObj,
clipboardItem.animCurveType(),
verboseUnits)) {
return (MS::kFailure);
}
}
return (MS::kSuccess);
}
bool animWriter::writeHeader(ofstream& clip)
{
if (!clip) {
return false;
}
clip << kAnimVersion << kSpaceChar << kAnimVersionString << kSemiColonChar << endl;
clip << kMayaVersion << kSpaceChar << MGlobal::mayaVersion() << kSemiColonChar << endl;
static MAnimCurveClipboard &clipboard =
MAnimCurveClipboard::theAPIClipboard();
double startTime = clipboard.startTime().as(timeUnit);
double endTime = clipboard.endTime().as(timeUnit);
if (startTime != endTime) {
MString unit;
animUnitNames::setToShortName(timeUnit, unit);
clip << kTimeUnit << kSpaceChar << unit << kSemiColonChar << endl;
animUnitNames::setToShortName(linearUnit, unit);
clip << kLinearUnit << kSpaceChar << unit << kSemiColonChar << endl;
animUnitNames::setToShortName(angularUnit, unit);
clip << kAngularUnit << kSpaceChar << unit << kSemiColonChar << endl;
clip << kStartTime << kSpaceChar << startTime << kSemiColonChar << endl;
clip << kEndTime << kSpaceChar << endTime << kSemiColonChar << endl;
}
float startUnitless = clipboard.startUnitlessInput();
float endUnitless = clipboard.endUnitlessInput();
if (startUnitless != endUnitless) {
clip << kStartUnitless << kSpaceChar << startUnitless <<
kSemiColonChar << endl;
clip << kEndUnitless << kSpaceChar << endUnitless <<
kSemiColonChar << endl;
}
return true;
}
bool animWriter::writeAnim( ofstream &clip,
const MAnimCurveClipboardItem &clipboardItem,
bool placeHolder ,
bool nodeNames )
{
if (!clip) {
return false;
}
clip << kAnim;
if (placeHolder) {
clip << kSpaceChar << clipboardItem.nodeName().asChar();
} else {
clip << kSpaceChar << clipboardItem.fullAttributeName().asChar();
if (nodeNames) {
clip << kSpaceChar << clipboardItem.leafAttributeName().asChar();
clip << kSpaceChar << clipboardItem.nodeName().asChar();
}
}
unsigned int rowCount, childCount, attrCount;
clipboardItem.getAddressingInfo(rowCount, childCount, attrCount);
clip << kSpaceChar << rowCount;
clip << kSpaceChar << childCount;
clip << kSpaceChar << attrCount;
clip << kSemiColonChar << endl;
return true;
}
bool animWriter::writeAnimCurve(ofstream &clip,
const MObject *animCurveObj,
MFnAnimCurve::AnimCurveType type,
bool verboseUnits )
{
if (NULL == animCurveObj || animCurveObj->isNull() || !clip) {
return true;
}
MStatus status = MS::kSuccess;
MFnAnimCurve animCurve(*animCurveObj, &status);
if (MS::kSuccess != status) {
MString msg = MStringResource::getString(kCouldNotExport, status);
MGlobal::displayError(msg);
return false;
}
clip << kAnimData << kSpaceChar << kBraceLeftChar << endl;
clip << kTwoSpace << kInputString << kSpaceChar <<
boolInputTypeAsWord(animCurve.isUnitlessInput()) <<
kSemiColonChar << endl;
clip << kTwoSpace << kOutputString << kSpaceChar <<
outputTypeAsWord(type) << kSemiColonChar << endl;
clip << kTwoSpace << kWeightedString << kSpaceChar <<
(animCurve.isWeighted() ? 1 : 0) << kSemiColonChar << endl;
if (verboseUnits) {
clip << kTwoSpace << kInputUnitString << kSpaceChar;
if (animCurve.isTimeInput()) {
MString unitName;
animUnitNames::setToShortName(timeUnit, unitName);
clip << unitName;
} else {
clip << kUnitlessString;
}
clip << kSemiColonChar << endl;
clip << kTwoSpace << kOutputUnitString << kSpaceChar;
}
double conversion = 1.0;
MString unitName;
switch (type) {
case MFnAnimCurve::kAnimCurveTA:
case MFnAnimCurve::kAnimCurveUA:
animUnitNames::setToShortName(angularUnit, unitName);
if (verboseUnits) clip << unitName;
{
MAngle angle(1.0);
conversion = angle.as(angularUnit);
}
break;
case MFnAnimCurve::kAnimCurveTL:
case MFnAnimCurve::kAnimCurveUL:
animUnitNames::setToShortName(linearUnit, unitName);
if (verboseUnits) clip << unitName;
{
MDistance distance(1.0);
conversion = distance.as(linearUnit);
}
break;
case MFnAnimCurve::kAnimCurveTT:
case MFnAnimCurve::kAnimCurveUT:
animUnitNames::setToShortName(timeUnit, unitName);
if (verboseUnits) clip << unitName;
break;
default:
if (verboseUnits) clip << kUnitlessString;
break;
}
if (verboseUnits) clip << kSemiColonChar << endl;
if (verboseUnits) {
MString angleUnitName;
animUnitNames::setToShortName(angularUnit, angleUnitName);
clip << kTwoSpace << kTanAngleUnitString <<
kSpaceChar << angleUnitName << kSemiColonChar << endl;
}
clip << kTwoSpace << kPreInfinityString << kSpaceChar <<
infinityTypeAsWord(animCurve.preInfinityType()) <<
kSemiColonChar << endl;
clip << kTwoSpace << kPostInfinityString << kSpaceChar <<
infinityTypeAsWord(animCurve.postInfinityType()) <<
kSemiColonChar << endl;
clip << kTwoSpace << kKeysString << kSpaceChar << kBraceLeftChar << endl;
unsigned numKeys = animCurve.numKeyframes();
for (unsigned i = 0; i < numKeys; i++) {
clip << kTwoSpace << kTwoSpace;
if (animCurve.isUnitlessInput()) {
clip << animCurve.unitlessInput(i);
}
else {
clip << animCurve.time(i).value();
}
double animValue = (conversion*animCurve.value(i));
if (animBase::isEquivalent(animValue,0.0)) animValue = 0.0;
clip << kSpaceChar << animValue;
clip << kSpaceChar << tangentTypeAsWord(animCurve.inTangentType(i));
clip << kSpaceChar << tangentTypeAsWord(animCurve.outTangentType(i));
clip << kSpaceChar << (animCurve.tangentsLocked(i) ? 1 : 0);
clip << kSpaceChar << (animCurve.weightsLocked(i) ? 1 : 0);
clip << kSpaceChar << (animCurve.isBreakdown(i) ? 1 : 0);
if (animCurve.inTangentType(i) == MFnAnimCurve::kTangentFixed) {
MAngle angle;
double weight;
animCurve.getTangent(i, angle, weight, true);
clip << kSpaceChar << angle.as(angularUnit);
clip << kSpaceChar << weight;
}
if (animCurve.outTangentType(i) == MFnAnimCurve::kTangentFixed) {
MAngle angle;
double weight;
animCurve.getTangent(i, angle, weight, false);
clip << kSpaceChar << angle.as(angularUnit);
clip << kSpaceChar << weight;
}
clip << kSemiColonChar << endl;
}
clip << kTwoSpace << kBraceRightChar << endl;
clip << kBraceRightChar << endl;
return true;
}