animExportUtil/animExportUtil.cpp

animExportUtil/animExportUtil.cpp
//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+
//
// File Name: animExportUtil.h
//
// Description: an animation export utility which illustrates how to
// use the MAnimUtil animation helper class, as well as how to export
// animation using the Maya .anim format
//
//
// *****************************************************************************
// INCLUDED HEADER FILES
#include <maya/MFStream.h>
#include <stdlib.h>
#include <string.h>
#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MFnPlugin.h>
#include <maya/MPxFileTranslator.h>
#include <maya/MDagPath.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MObjectArray.h>
#include <maya/MItDag.h>
#include <maya/MItDependencyNodes.h>
#include <maya/MFnAttribute.h>
#include <maya/MFnSet.h>
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>
#include <maya/MFnAnimCurve.h>
#include <maya/MAnimUtil.h>
#include "animExportUtil.h"
#include "animFileExport.h"
#if defined (OSMac_)
using namespace std;
extern "C" int strcasecmp (const char *, const char *);
#endif
// *****************************************************************************
// HELPER METHODS
// *****************************************************************************
#ifndef min
static inline double
min (double a, double b)
{
return (a < b ? a : b);
}
#endif
#ifndef max
static inline double
max (double a, double b)
{
return (a > b ? a : b);
}
#endif
// *****************************************************************************
// PUBLIC MEMBER FUNCTIONS
TanimExportUtil::TanimExportUtil()
{
}
TanimExportUtil::~TanimExportUtil()
{
}
TanimExportUtil::writer (
const MFileObject &file,
const MString &/*options*/,
FileAccessMode mode
)
{
// Create the export file
//
ofstream animFile (file.fullName().asChar());
// Create a selection list to hold the objects we want to export
//
if (mode == kExportActiveAccessMode) {
// Use the active list
//
}
else {
// Create a selection list with all the objects in the world
//
// Add the top level dag objects
//
for (dagIt.next(); !dagIt.isDone(); dagIt.next()) {
MDagPath path;
if (dagIt.getPath (path) != MS::kSuccess) {
continue;
}
list.add (path);
// We only want the top level objects (since we will walk
// down the hierarchy later
//
dagIt.prune ();
}
// Gather the rest of the dependency nodes
//
for (; !nodeIt.isDone(); nodeIt.next()) {
MObject node = nodeIt.item();
if (node.isNull()) {
continue;
}
// We have already saved the dag objects, so skip them here
//
if (node.hasFn (MFn::kDagNode)) {
continue;
}
// Watch out for characters, and only write the top level
// character
//
if (node.hasFn (MFn::kCharacter)) {
// Find out which (if any) sets this node belongs to
//
// Get the message attribute
//
MFnDependencyNode fnNode (node);
MObject aMessage = fnNode.attribute (MString ("message"));
MPlug messagePlug (node, aMessage);
// Now find what it is connected to as a source plug
//
MPlugArray srcPlugArray;
messagePlug.connectedTo (srcPlugArray, false, true);
// Now walk through each connection and see if any is to
// another character
//
unsigned int numPlugs = srcPlugArray.length();
bool belongsToCharacter = false;
for (unsigned int i = 0; (i < numPlugs) && !belongsToCharacter; i++) {
const MPlug &plug = srcPlugArray[i];
if (!plug.node().hasFn (MFn::kCharacter)) {
continue;
}
belongsToCharacter = true;
}
if (!belongsToCharacter) {
list.add (node);
}
continue;
}
// A superfluous test to filter out unanimated objects, just to
// show how to use MAnimUtil
//
if (!MAnimUtil::isAnimated (node)) {
continue;
}
list.add (node);
}
}
// The Maya .anim format needs to know the bounds of the animation
// since it could be used in pasteKey operations (which support
// scaling in time), so gather that information now
//
// Find all the plugs that are animated in our selection list
//
MPlugArray animatedPlugs;
MAnimUtil::findAnimatedPlugs (list, animatedPlugs);
unsigned int numPlugs = animatedPlugs.length();
bool hasTime = false;
double startTime = 0.0;
double endTime = 0.0;
bool hasUnitless = false;
double startUnitless = 0.0;
double endUnitless = 0.0;
unsigned int i;
// For each animated plug, determine the bounds of the animation
//
for (i = 0; i < numPlugs; i++) {
MPlug plug = animatedPlugs[i];
MObjectArray animation;
// Find the animation curve(s) that animate this plug
//
if (!MAnimUtil::findAnimation (plug, animation)) {
continue;
}
unsigned int numCurves = animation.length();
for (unsigned int j = 0; j < numCurves; j++) {
MObject animCurveNode = animation[j];
if (!animCurveNode.hasFn (MFn::kAnimCurve)) {
continue;
}
MFnAnimCurve animCurve (animCurveNode);
unsigned int numKeys = animCurve.numKeys();
if (numKeys == 0) {
continue;
}
if (animCurve.isUnitlessInput()) {
if (!hasUnitless) {
startUnitless = animCurve.unitlessInput (0);
endUnitless = animCurve.unitlessInput (numKeys - 1);
hasUnitless = true;
}
else {
startUnitless = min (startUnitless, animCurve.unitlessInput (0));
endUnitless = max (endUnitless, animCurve.unitlessInput (numKeys - 1));
}
}
else {
if (!hasTime) {
startTime = animCurve.time (0).value();
endTime = animCurve.time (numKeys - 1).value();
hasTime = true;
}
else {
startTime = min (startTime, animCurve.time (0).value());
endTime = max (endTime, animCurve.time (numKeys - 1).value());
}
}
}
}
// Write out the header information
//
animWriter writer;
writer.writeHeader (animFile, startTime, endTime, startUnitless, endUnitless);
// Now write out the animation for each object on the selection list
//
unsigned int numObjects = list.length();
for (i = 0; i < numObjects; i++) {
MDagPath path;
MObject node;
if (list.getDagPath (i, path) == MS::kSuccess) {
write (animFile, path);
}
else if (list.getDependNode (i, node) == MS::kSuccess) {
write (animFile, node);
}
}
animFile.flush();
animFile.close();
return (MS::kSuccess);
}
void
TanimExportUtil::write (ofstream &animFile, const MDagPath &path)
{
// Walk through the dag breadth first
//
dagIt.reset (path, MItDag::kDepthFirst);
for (; !dagIt.isDone(); dagIt.next()) {
MDagPath thisPath;
if (dagIt.getPath (thisPath) != MS::kSuccess) {
continue;
}
// Find the animated plugs for this object
//
MPlugArray animatedPlugs;
MObject node = thisPath.node();
MFnDependencyNode fnNode (node);
MAnimUtil::findAnimatedPlugs (thisPath, animatedPlugs);
unsigned int numPlugs = animatedPlugs.length();
if (numPlugs == 0) {
// If the object is not animated, then write out place holder
// information
//
animFile << "anim " << fnNode.name().asChar() << " " << dagIt.depth() << " " << thisPath.childCount() << " 0;\n";
}
else {
// Otherwise write out each animation curve
//
writeAnimatedPlugs (animFile, animatedPlugs, fnNode.name(), dagIt.depth(), thisPath.childCount());
}
}
}
void
TanimExportUtil::write (ofstream &animFile, const MObject &node)
{
// Watch out for characters and handle them a little differently
//
if (node.hasFn (MFn::kCharacter)) {
MObjectArray characterList;
characterList.append (node);
MIntArray depths;
depths.append (0);
unsigned int current = 0;
while (current < characterList.length()) {
const MObject &thisNode = characterList[current];
int thisDepth = depths[current++];
// If this node is a character, then check for any immediate
// subCharacters
//
MFnSet fnSet (thisNode);
// Now find the set members
//
MSelectionList members;
fnSet.getMembers (members, false);
unsigned int childCount = 0;
for (; !iter.isDone(); iter.next()) {
MObject childNode;
iter.getDependNode (childNode);
characterList.insert (childNode, current + childCount);
depths.insert (thisDepth + 1, current + childCount);
childCount++;
}
// Find the animated plugs for this object
//
MPlugArray animatedPlugs;
MAnimUtil::findAnimatedPlugs (thisNode, animatedPlugs);
unsigned int numPlugs = animatedPlugs.length();
if (numPlugs == 0) {
// If the object is not animated, then write out place holder
// information
//
animFile << "anim " << fnSet.name().asChar() << " " << thisDepth << " " << childCount << " 0;\n";
}
else {
// Otherwise write out each animation curve
//
writeAnimatedPlugs (animFile, animatedPlugs, fnSet.name(), thisDepth, childCount);
}
}
return;
}
// Find the animated plugs for this object
//
MPlugArray animatedPlugs;
MFnDependencyNode fnNode (node);
MAnimUtil::findAnimatedPlugs (node, animatedPlugs);
unsigned int numPlugs = animatedPlugs.length();
if (numPlugs != 0) {
// If the object is animated the write out each animation curve
//
writeAnimatedPlugs (animFile, animatedPlugs, fnNode.name(), 0, 0);
}
}
void
TanimExportUtil::writeAnimatedPlugs (
ofstream &animFile,
const MPlugArray &animatedPlugs,
const MString &nodeName,
unsigned int depth,
unsigned int childCount
)
{
// Walk through each animated plug and write out the animation curve(s)
//
unsigned int numPlugs = animatedPlugs.length();
for (unsigned int i = 0; i < numPlugs; i++) {
MPlug plug = animatedPlugs[i];
MObjectArray animation;
if (!MAnimUtil::findAnimation (plug, animation)) {
continue;
}
// Write out the plugs' anim statement
//
animFile << "anim ";
// build up the full attribute name
//
MPlug attrPlug (plug);
MObject attrObj = attrPlug.attribute();
MFnAttribute fnAttr (attrObj);
MString fullAttrName (fnAttr.name());
attrPlug = attrPlug.parent();
while (!attrPlug.isNull()) {
attrObj = attrPlug.attribute();
MFnAttribute fnAttr2 (attrObj);
fullAttrName = fnAttr2.name() + "." + fullAttrName;
attrPlug = attrPlug.parent();
}
attrObj = plug.attribute();
MFnAttribute fnLeafAttr (attrObj);
animFile << fullAttrName.asChar() << " " << fnLeafAttr.name().asChar() << " " << nodeName.asChar() << " " << depth << " " << childCount << " " << i << ";\n";
unsigned int numCurves = animation.length();
// Write out each animation curve that animates the plug
//
for (unsigned int j = 0; j < numCurves; j++) {
MObject animCurveNode = animation[j];
if (!animCurveNode.hasFn (MFn::kAnimCurve)) {
continue;
}
animWriter writer;
writer.writeAnimCurve (animFile, &animCurveNode);
}
}
}
bool
TanimExportUtil::haveWriteMethod () const
{
return (true);
}
TanimExportUtil::defaultExtension () const
{
return (MString("anim"));
}
TanimExportUtil::identifyFile (
const MFileObject &file,
const char * /*buffer*/,
short /*size*/
) const
{
const char *name = file.name().asChar();
int nameLength = (int)strlen(name);
if ((nameLength > 5) && !strcasecmp(name+nameLength-5, ".anim")) {
return (kIsMyFileType);
}
return (kNotMyFileType);
}
/* static */ void *
TanimExportUtil::creator ()
{
return (new TanimExportUtil);
}
//--------------------------------------------------------------------------------
// Plugin management
//--------------------------------------------------------------------------------
initializePlugin (MObject obj)
{
MStatus status;
MFnPlugin plugin (obj, PLUGIN_COMPANY, "3.0");
status = plugin.registerFileTranslator ("animExportUtil", "", TanimExportUtil::creator);
return (status);
}
uninitializePlugin (MObject obj)
{
MFnPlugin plugin (obj);
plugin.deregisterFileTranslator ("animExportUtil");
return (MS::kSuccess);
}