atomImportExport/atomAnimLayers.cpp

atomImportExport/atomAnimLayers.cpp
//
// File Name: atomAnimLayers.cpp
//
//
// Information on animation layers, used by import and export.
#include <maya/MFnAttribute.h>
#include <maya/MAnimCurveClipboard.h>
#include <maya/MGlobal.h>
#include <string.h>
#include "atomAnimLayers.h"
#include "atomImportExportStrings.h"
const char *kAnimLayers = "animLayers";
//return true if we have animLayers in the scene
bool atomAnimLayers::getOrderedAnimLayers()
{
MString melCommand = MString("atomGetAllLayersOrdered();");
mOrderedAnimLayerNames.setLength(0);
status = MGlobal::executeCommand(melCommand, mOrderedAnimLayerNames, false, false );
return (status==MS::kSuccess && mOrderedAnimLayerNames.length() >0);
}
//given a specified layerName and the name of the previous layer it follows,
//create the layer
void atomAnimLayers::createAnimLayer(const MString &layerName, const MString &prevLayerName)
{
if(prevLayerName.length() !=0)
{
//move the created layer to after the previous
MString melCommand = MString("animLayer -mva ") + MString("\"") + prevLayerName + MString("\"") + MString(" ") + MString("\"")+
layerName + MString("\"")+ MString(";");
status = MGlobal::executeCommand(melCommand, false, false );
}
else
{
if(layerName == MString("BaseAnimation")) //todo need a better way to handle the root layer
{
MString melCommand("animLayer;");
status = MGlobal::executeCommand(melCommand, false, false );
}
else //not the root so just create it, it will go at the end which is fine since we don't know where it shoudl go in the list.
{
MString melCommand = MString("animLayer ") + MString("\"") + layerName + MString("\"") + MString(";");
status = MGlobal::executeCommand(melCommand, false, false );
}
}
}
//check and if the specified attrName on the specified nodeName exists on the specified layerName
bool atomAnimLayers::isAttrInAnimLayer(const MString &nodeName, const MString &attrName, const MString &layerName)
{
MStringArray resultArray;
MString melCommand = MString("animLayer -query -attribute ") + MString("\"") + layerName + MString("\"");
status = MGlobal::executeCommand(melCommand, resultArray, false, false );
MString name = nodeName + MString(".") + attrName;
for(unsigned int k = 0; k < resultArray.length(); ++k)
{
if(name == resultArray[k])
return true;
}
return false;
}
//add the specified nodename.attrName to the specified layer
bool atomAnimLayers::addAttrToAnimLayer(const MString &nodeName, const MString &attrName, const MString &layerName)
{
MString name = nodeName + MString(".") + attrName;
MString melCommand = MString("animLayer -edit -attribute ") + MString("\"") + name + MString("\"") + MString(" ")
+ MString("\"") + layerName + MString("\"");
status = MGlobal::executeCommand(melCommand, false, false );
return (status==MS::kSuccess);
}
//remove this attribute from all layers
void atomAnimLayers::removeLayersIfNeeded(bool replaceLayers, const MString &nodeName, const MString &attrName)
{
//if we are replacing and we have layers in the scene
if(replaceLayers == true && mOrderedAnimLayerNames.length() > 0)
{
MString fullName = nodeName + MString(".") + attrName;
std::string str(fullName.asChar());
if(mAttrsRemovedFromAnimLayers.find(std::string(str)) == mAttrsRemovedFromAnimLayers.end())
{
mAttrsRemovedFromAnimLayers.insert(str); //don't remove it again
//mOrderedAnimLayers was caclculated when we read in the animLayer names.
for(unsigned int z = 0; z < mOrderedAnimLayerNames.length(); ++z)
{
MString layerName = mOrderedAnimLayerNames[z];
if(isAttrInAnimLayer(nodeName, attrName, layerName)) //if the attribute is in this layer remove it.
{
MString melCommand = MString("animLayer -edit -removeAttribute ") + MString("\"") + fullName + MString("\"") + MString(" ") + MString("\"")+
layerName + MString("\"")+ MString(";");
MStatus status = MGlobal::executeCommand(melCommand, false, false );
if(status == MS::kSuccess)
{
//removed that attribute so save the layer name out. later after loading we will delete these layers that have
//no attributes left in them.
std::string layerStr(layerName.asChar());
if(mLayersWithRemovedAttrs.find(std::string(layerStr)) == mLayersWithRemovedAttrs.end())
mLayersWithRemovedAttrs.insert(layerStr);
}
}
}
}
}
}
void atomAnimLayers::deleteEmptyLayers(bool replaceLayers)
{
if(replaceLayers == true && mLayersWithRemovedAttrs.size() > 0)
{
for(std::set<std::string>::iterator iter = mLayersWithRemovedAttrs.begin(); iter !=
mLayersWithRemovedAttrs.end(); ++iter)
{
std::string str = *iter;
MString layerName(str.c_str());
MStringArray resultArray;
MString melCommand = MString("animLayer -query -attribute ") + MString("\"") + layerName + MString("\"");
MGlobal::executeCommand(melCommand, resultArray, false, false );
if(resultArray.length() ==0) //it has no attributes so delete it..
{
melCommand = MString("delete ") + MString("\"") + layerName + MString("\"");
MGlobal::executeCommand(melCommand,false,false);
}
}
}
}
//this function creates missing animation layers and places it in the correct order
void atomAnimLayers::createMissingAnimLayers(const MStringArray &animLayers)
{
for(unsigned int k=0; k < animLayers.length(); ++k)
{
list.add(animLayers[k]);
if(list.length()!=1) //if not in the list then not in the scene so create it, and the previous one WILL be in the scene so attach to it
{
MString prevLayerName;
if(k>0)
prevLayerName = animLayers[k-1];
//todo need a better way to handle the root layer
//this fixes an issue where when you create an animation layer for the base it creates 2 layers the BaseAnimation
//and a default animLayer1, where if you create the same thing in the UI the second one is named AnimLayer1, with
//a cap A. So we are getting an extra animLayer1 created, which is a little messy. This stops that
//but we need to revisit. Also seems like you can't rename BaseAnimation, but not sure what it is in different
//languages. It's a node so maybe it never changes?
if(k > 0 || animLayers[k] != MString("BaseAnimation") || animLayers.length()==0) //we know we are creating more than 1 so the BaseAnimation will
{ //be created next time so dont create it unless only creating a BaseAnimation
createAnimLayer(animLayers[k],prevLayerName);
}
}
}
}
//find all animation layers in the scene and add them to the selection list
void atomAnimLayers::addAnimLayersToSelection()
{
getOrderedAnimLayers();
for(unsigned int z = 0; z < mOrderedAnimLayerNames.length();++z)
{
MGlobal::selectByName( mOrderedAnimLayerNames[z], MGlobal::kAddToList );
}
}
//this function will pass in all anim layer objects found on a particular plug. since we want them to be
//kept in order, but don't want ALL of the anim layers to be specified if they aren't used we
bool atomAnimLayers::addAnimLayers(MObjectArray &layers)
{
if(mOrderedAnimLayerNames.length() > 0)
{
//first time called set up the mAnimLayers object array.
if(mAnimLayers.length() != mOrderedAnimLayerNames.length())
{
mAnimLayers.setLength(mOrderedAnimLayerNames.length());
MObject nullObject;
//initialize with nulls
for(unsigned int k = 0; k < mAnimLayers.length(); ++k)
{
mAnimLayers[k] = nullObject;
}
}
}
//if here we have the ordered name list and the anim layer object list
//now we just need to set the objects passed in correctly
for(unsigned int k = 0; k < layers.length(); ++k)
{
if(layers[k].hasFn (MFn::kDependencyNode))
{
MFnDependencyNode fnNode (layers[k]);
MString layerName = fnNode.name();
for(unsigned int z = 0; z < mOrderedAnimLayerNames.length();++z)
{
if(layerName == mOrderedAnimLayerNames[z])
{
mAnimLayers[z] = layers[k];
break;
}
}
}
}
return true;
}
//this function adds the layer objects in mAnimLayers to the start of the selection list.
//this is used when exporting to make sure the animationlayers come first
void atomAnimLayers::addLayersToStartOfSelectionList(MSelectionList &list)
{
if(mAnimLayers.length() > 0 )
{
for(unsigned int i =0; i<mAnimLayers.length();++i)
{
layers.add(mAnimLayers[i], true);
}
layers.merge(list);
list = layers;
}
}
//for the nthArray that we hold(that corresponds to the nth item in the export list),
//get the animation layer plugs for it's attributes, e.g. weight, mute
void atomAnimLayers::getPlugs(unsigned int nth, MPlugArray &plugs)
{
if(nth < mAnimLayers.length() && mAnimLayers[nth].hasFn (MFn::kDependencyNode))
{
MFnDependencyNode fnNode (mAnimLayers[nth]);
MStringArray attributes;
getRelevantAttributes(attributes);
collectAnimLayerPlugs(fnNode,attributes,plugs);
}
}
//get attribute names for an animationlayer, like mute,weight, solo
void atomAnimLayers::getRelevantAttributes(MStringArray &attributes)
{
MString attr("mute");
attributes.append(attr);
attr = MString("lock");
attributes.append(attr);
attr = MString("solo");
attributes.append(attr);
attr = MString("override");
attributes.append(attr);
attr = MString("passthrough");
attributes.append(attr);
attr = MString("preferred");
attributes.append(attr);
attr = MString("weight");
attributes.append(attr);
attr = MString("rotationAccumulationMode");
attributes.append(attr);
attr = MString("scaleAccumulationMode");
attributes.append(attr);
}
//get the attribute plugs for a specific layer
void atomAnimLayers::collectAnimLayerPlugs(MFnDependencyNode &layer, MStringArray &attributes, MPlugArray &plugs)
{
for(unsigned int i = 0; i < attributes.length(); ++i)
{
MPlug plug = layer.findPlug(attributes[i],false, &status);
if(status== MS::kSuccess)
{
plugs.append(plug);
}
}
}
//for each animation layer we have, write out the name of the layer
//we will later write out the layer like a depend node and write out
//each attribute, but we do this seperately so that on import we
//can quickly see what layers are in the ATOM file so that we can
//create them if they are missing
void atomAnimLayers::writeAnimLayers(ofstream &animFile, atomWriter &writer)
{
if(mAnimLayers.length() > 0) // at least one Layer
{
animFile <<kAnimLayers << " { ";
for(unsigned int k = 0; k < mAnimLayers.length(); ++k)
{
if(mAnimLayers[k].hasFn (MFn::kDependencyNode))
{
MFnDependencyNode fnNode (mAnimLayers[k]);
MString layerName = fnNode.name();
animFile <<" " << layerName;
}
}
animFile << " }\n";
}
}
//read out the animation layers from the animReader
//then create any missing layers and add them to the active selection
bool atomAnimLayers::readAnimLayers(ifstream &readAnim, char *dataType,atomReader &reader)
{
if (strcmp(dataType, kAnimLayers) == 0)
{
dataType = reader.asWord(readAnim);
if (strcmp(dataType, "{") == 0)
{
MStringArray layerNames;
while((dataType = reader.asWord(readAnim)))
{
if(readAnim && ! readAnim.eof() && dataType && strcmp(dataType, "}") !=0)
{
MString name(dataType);
layerNames.append(name);
}
else
break;
}
if(layerNames.length() >0)
{
createMissingAnimLayers(layerNames);
addAnimLayersToSelection();
}
}
return true;
}
return false;
}
//
//
//
//
//for this attribute on this node add the following layer nodes and plugs it's associated with
void atomNodeWithAnimLayers::addPlugWithLayer(MPlug &attrPlug, MObjectArray &layers, MPlugArray &plugs)
{
if(plugs.length() == layers.length())
{
MObject attrObj = attrPlug.attribute();
MFnAttribute fnLeafAttr (attrObj);
std::string attrStd(std::string(fnLeafAttr.name().asChar()));
PlugsAndLayers plugsAndLayers;
for(unsigned int i =0; i < layers.length(); ++i)
{
if(plugs[i].isNull()==false) //it's possible to not have a plug for the specified layer
{
if(layers[i].hasFn (MFn::kDependencyNode))
{
MFnDependencyNode fnNode (layers[i]);
MString layerName = fnNode.name();
plugsAndLayers.mLayerNames.append(layerName);
plugsAndLayers.mPlugs.append(plugs[i]);
fAttrLayers[attrStd] = plugsAndLayers;
}
}
}
}
}
//is the specified plug on this node animation layered
//used when exporting
bool atomNodeWithAnimLayers::isPlugLayered(const MString &plugName, MStringArray &layerNames)
{
AttrLayersMap::iterator p;
std::string stdAttrName(plugName.asChar());
p = fAttrLayers.find(stdAttrName);
if(p != fAttrLayers.end())
{
PlugsAndLayers val = p->second;
layerNames = val.mLayerNames;
return true;
}
return false;
}
//
//
// atomLayerClipboard
//
//
//get the clipboarditemarray for the specified layername
//if the layer string is empty we return the default clipboard
MAnimCurveClipboardItemArray& atomLayerClipboard::getCBItemArray(const MString &layerName)
{
if(layerName.length() ==0)
return fEmptyLayerArray;
//here the layername is something
std::string name(layerName.asChar());
ArrayMap::iterator p;
p = fArray.find(name);
if(p != fArray.end())
{
return *(p->second);
}
fArray[name] = item;
return *item;
}
atomLayerClipboard::~atomLayerClipboard()
{
for(ArrayMap::iterator p = fArray.begin(); p!= fArray.end(); ++p)
{
if(p->second)
delete p->second;
}
}
//paste the specified clipboard, with the specified attributes
//if the animLayerName is empty we won't use the animLayer flag
//when pasting.
bool atomLayerClipboard::pasteClipboard(MAnimCurveClipboardItemArray &itemArray,
const MTime& startTime, const MTime& endTime,
float startUnitless, float endUnitless,const MString &pasteFlags,
MString &animLayerName)
{
bool good = false;
startTime,endTime,startUnitless, endUnitless,false))
{
good = true;
}
if (MAnimCurveClipboard::theAPIClipboard().isEmpty()== false)
{
MString command("pasteKey -cb api ");
//we always match by name since we do use our own local algorithm
//to match up hierarchies. So not matter what option we use
//the name of the items in the clipboard will match up as we expect them too.
command += " -mn true ";
if(animLayerName.length() >0)
{
MString onThisLayer = MString(" -al ") + animLayerName + MString(" ");
command += onThisLayer;
}
command += pasteFlags;
int result;
if (MS::kSuccess != (status =
MGlobal::executeCommand(command, result, false, true)))
{
MString msg = MStringResource::getString(kPasteFailed, status);
return false;
}
}
return good;
}
//paste the keys we have in each stored clipboard using the specified parameters.
//will paste the default layer and each animlayer
MStatus atomLayerClipboard::pasteKeys(const MTime& startTime, const MTime& endTime,
float startUnitless, float endUnitless,const MString &pasteFlags)
{
MString animLayerName; //empty first time for the first default empty layer
bool oneWasGood = pasteClipboard(fEmptyLayerArray, startTime,endTime,
startUnitless, endUnitless,pasteFlags,animLayerName);
//now go through and do each one that's in an animation layer
for(ArrayMap::iterator p = fArray.begin(); p!= fArray.end(); ++p)
{
if(p->second) //double check but should not be NULL
{
animLayerName= MString(p->first.c_str());
bool good = pasteClipboard(*(p->second), startTime,endTime,
startUnitless, endUnitless,pasteFlags,animLayerName);
if(good == true)
oneWasGood = true;
}
}
//if not one was good then something failed.
if(oneWasGood==false)
{
MStatus stringStat;
MString msg = MStringResource::getString(kClipboardFailure,
stringStat);
}
return (MS::kSuccess);
}
//does this specified node have any layers, and if so return them
//in a std::set of std::strings
bool atomNodeWithAnimLayers::isNodeLayered(std::set<std::string> &layerNames)
{
AttrLayersMap::iterator p;
bool isLayered = false;
for(p = fAttrLayers.begin();p!= fAttrLayers.end(); ++p)
{
PlugsAndLayers val = p->second;
for(unsigned int i =0 ;i< val.mLayerNames.length(); ++i)
{
if(val.mLayerNames[i].length() > 0)
{
std::string str(val.mLayerNames[i].asChar());
layerNames.insert(str);
isLayered = true;
}
}
}
return isLayered;
}