写这个东西的缘由是一个同事给我说了一个他以前碰到的一个问题:一个文件里有上万条动画曲线,要删除某一帧前面的帧,也要删掉某一帧后面的帧,相当于是掐头去尾,但从功能上来说这个mel完全可以胜任,但是执行起来效率就特别的低,于是他找另一个同事帮他写了个command,这样就将一个6G的含有上万条动画曲线的文件,只用了40多分钟就删完了,最后只剩下1G,而用mel删了一个小时还不到10%,可见二者效率相差。于是自己也想试着写一些,真正写起来没啥难点,主要是两个类,MFnAnimCurve和MAnimCurveChange,同时也实现了redo和undo,实现这个主要是用一个指针在堆中创建一个内存空间,然后将removekey都缓存到这块内存空间里,最后用它自身方法就可以实现redo和undo,自己测试,我这个command效率上没有他的那个夸张,应该是有些地方还不够优化,后面再继续完善吧。想想自己和他们的距离,不,那是天堑,自己还得多加努力啊。具体代码如下:
#include<maya/MPxCommand.h>
#include <maya/MFnPlugin.h>
#include <maya/MSyntax.h>
#include <maya/MDistance.h>
#include <maya/MItDependencyNodes.h>
#include <maya/MDGModifier.h>
#include <maya/MArgDatabase.h>
#include <maya/MFnAnimCurve.h>
#include <maya/MAnimCurveChange.h>
class DeleteKeysCmd : public MPxCommand
{
public:
DeleteKeysCmd();
~DeleteKeysCmd();
virtual MStatus doIt( const MArgList& );
virtual MStatus redoIt();
virtual MStatus undoIt();
virtual bool isUndoable() const { return true; };
static void *creator(){ return new DeleteKeysCmd; }
static MSyntax newSyntax();
private:
MTime startFrame;
MTime endFrame;
MAnimCurveChange* pAnimCache;
};
const char *startFrameFlag = "-sf", *startFrameLongFlag = "-startFrame";
const char *newendFrameFlag = "-ef", *newendFrameLongFlag = "-endFrame";
DeleteKeysCmd::DeleteKeysCmd()
{
startFrame.setValue( 0 );
endFrame.setValue( 100 );
}
DeleteKeysCmd::~DeleteKeysCmd()
{
delete pAnimCache;
}
MSyntax DeleteKeysCmd::newSyntax()
{
MSyntax syntax;
syntax.addFlag( startFrameFlag, startFrameLongFlag, MSyntax::kLong );
syntax.addFlag( newendFrameFlag, newendFrameLongFlag, MSyntax::kLong );
return syntax;
}
MStatus DeleteKeysCmd::doIt( const MArgList &args )
{
MStatus stat;
pAnimCache = NULL;
pAnimCache = new MAnimCurveChange();
MArgDatabase argData( syntax(), args, &stat );
if( !stat )
return stat;
if( argData.isFlagSet( startFrameFlag ) )
argData.getFlagArgument( startFrameFlag, 0, startFrame);
if( argData.isFlagSet( newendFrameLongFlag ) )
argData.getFlagArgument( newendFrameLongFlag, 0, endFrame);
MDGModifier modifier;
MItDependencyNodes animCurves(MFn::kAnimCurve);
for (; !animCurves.isDone(); animCurves.next())
{
MObject currentItem = animCurves.item();
if ( currentItem.isNull() )
{
continue;
}
MFnAnimCurve fnCurve (currentItem);
unsigned int numKeys = fnCurve.numKeys();
if (numKeys == 0)
{
modifier.deleteNode(currentItem);
}
else
{
unsigned int startFrameIndex = fnCurve.findClosest(startFrame);
for(unsigned int i = 0; i < startFrameIndex; i++)
{
fnCurve.remove(0, pAnimCache);
}
unsigned int endFrameIndex = fnCurve.findClosest(endFrame);
unsigned int numKeysToRemoves = numKeys -1 - endFrameIndex;
for(unsigned int i = 0; i < numKeysToRemoves; i++)
{
fnCurve.remove(endFrameIndex + 1, pAnimCache);
}
}
}
return stat;
}
MStatus DeleteKeysCmd::undoIt()
{
if( pAnimCache != NULL )
pAnimCache -> undoIt();
return MS::kSuccess;
}
MStatus DeleteKeysCmd::redoIt()
{
if( pAnimCache != NULL )
pAnimCache -> redoIt();
return MS::kSuccess;
}
MStatus initializePlugin( MObject obj )
{
MFnPlugin plugin( obj, "Lulongfei", "1.0" );
MStatus stat;
stat = plugin.registerCommand( "deleteKeys", DeleteKeysCmd::creator, DeleteKeysCmd::newSyntax );
if ( !stat )
stat.perror( "registerCommand failed" );
return stat;
}
MStatus uninitializePlugin( MObject obj )
{
MFnPlugin plugin( obj );
MStatus stat;
stat = plugin.deregisterCommand( "deleteKeys" );
if ( !stat )
stat.perror( "deregisterCommand failed" );
return stat;
}
def removeInvalidKeys(start, end):
animCurves = om.MItDependencyNodes(om.MFn.kAnimCurve)
while not animCurves.isDone():
modifier = om.MDGModifier()
fnCurveCache = oma.MAnimCurveChange()
currentItem = animCurves.item()
fnCurve = oma.MFnAnimCurve(currentItem)
if fnCurve.numKeys():
# remove all frame before the start keyframe
startFrameIndex = fnCurve.findClosest(om.MTime(start))
for i in xrange(startFrameIndex):
fnCurve.remove(0, fnCurveCache)
# remove all frame after the end keyframe
endFrameIndex = fnCurve.findClosest(om.MTime(end))
for i in xrange(endFrameIndex, fnCurve.numKeys() - 1):
fnCurve.remove(endFrameIndex + 1, fnCurveCache)
#fnCurveCache.undoIt()
else:
modifier.deleteNode(currentItem)
animCurves.next()
具体使用时cmd可以用:cmds.deleteKeys(sf = 20, ef = 90); mel用deleteKeys -sf 20 -ef 90
而python则直接可以调用function了,removeInvalidKeys(20, 90)