MPxCommand - Adding Custom mel commands

 

   

 

 

 

Defining the Mel Command

Adding your own functions into mel is a relatively simple thing to do. We have to derive our own class from MPxCommand which will handle the calls to our functions. In addition to the MPxCommand proxy class, we also have the MArgList, MArgDatabase and MSyntax utility classes which greatly simplify the process of parsing the arguments passed to the function. More on that later ;)

Right then, lets start with the simplest possible code for a new mel function. We simply need to provide a class derived from MPxCommand. This class then needs to impliment a doIt function to execute the required tasks, and we also need a creator function to return a new instance of our function class whenever the mel function is called.

As maya executes mel commands, it keeps an undo list of the last x number of commands executed (the exact amount specified in the user preferences). This basically means that when our command is called, a new instance of the MyCommand class is created. Later we will see that this is used to store a list of instructions to undo our command. For the moment though, we will not worry about undoing things, we'll keep it simple instead ;)

 

 

 


 // When compiling under Win32, we need to define NT_PLUGIN or NT_APP before including
 // and maya headers. Under linux we need LINUX to be defined, this however is done 
 // for you in the mayacc and mayald scripts.
 // 
 #ifdef WIN32
     #define NT_PLUGIN
 #endif  

 #include <maya/MPxCommand.h>
 #include <maya/MGlobal.h>
 #include <maya/MArgList.h>
 #include <maya/MFnDependencyNode.h>
 #include <maya/MItDependencyNodes.h>

 class MyCommand : public MPxCommand {
 public:

	/// \brief	executes our function
	/// \param	args	-	the argument list specified
	/// \return	MS::kSuccess or MS::kFailure
	///
	virtual MStatus doIt( const MArgList& args ) ;
	
	/// \brief	This function is used by maya to create a new instance of the function
	/// 		object, each time the command is called. This only really makes sense 
	/// 		later when dealing with undo and redo. The function object will create a
	/// 		list of things to undo so maya can store this command in it's history.
	/// \return	a new instance of this function
	/// 
	static void* creator() {
		return new MyCommand;
	}
 };

 MStatus MyCommand::doIt( const MArgList& args ) {
	// iterate through all nodes in the scene
	MItDependencyNodes it;
	while( !it.isDone() )
	{
		// attach a dependency node function set to the object
		MFnDependencyNode fn(it.item());
		
		// print the name
		MGlobal::displayInfo( fn.name().asChar() );
		
		// next item please!
		it.next();
	}
	return MS::kSuccess;
 }



 

 

 

Registering the Function

As with all new nodes, commands etc that we add into Maya via a plugin, we have to use the initialisePlugin and uninitialzePlugin methods to register and deregister our mel command.

For this we simply need to use the registerCommand and deregisterCommand methods of the MFnPlugin function set.

 

 

 


 #include "MyCommand.h"
 #include <maya/MFnPlugin.h>


 // This is a nasty bit of hackyness for compilation under Windows. Under Win32 you need 
 // to compile a dll project and change the extension from "dll" to "mll". One additional
 // thing we have to do is 'export' the initializePlugin and uninitializePlugin functions. 
 // This basically means that when maya loads the dll, it can see the two Functions it needs.
 // If for some reason your plugin fails to load, it may be this thats causing the problems.
 // Under linux we simply need to compile it with the -shared flag.
 // 
 #ifdef WIN32
     #define MLL_EXPORT __declspec(dllexport) 
 #else
     #define MLL_EXPORT
 #endif  
 
 //------------------------------------------------------------------- 
 /// \brief initializePlugin( MObject obj )
 /// \param obj - the plugin handle
 /// \return MS::kSuccess if ok 
 /// \note Registers all of the new commands, file translators and new
 /// node types. 
 /// 
 MLL_EXPORT MStatus initializePlugin(MObject obj ) { 
 	MFnPlugin plugin( obj, "Rob Bateman", "1.0", "Any");  

	// register the mel command with the plugin function set.
	// Do this for each mel command your plugin is going to add into Maya
	//
	MStatus status = plugin.registerCommand( "myCommand", MyCommand::creator ); 
	if (!status) 
	{
		status.perror("Failed to register \"myCommand\"\n"); 
		return status; 
	}
	return status; 
 }  

 //------------------------------------------------------------------- 
 /// \brief	uninitializePlugin( MObject obj ) 
 /// \param	obj - the plugin handle to un-register
 /// \return	MS::kSuccess if ok
 /// \note	un-registers the plugin and destroys itself 
 /// 
 MLL_EXPORT MStatus uninitializePlugin( MObject obj ) { 
	MFnPlugin plugin( obj );
	
	
	// deregister the mel command with the plugin function set
	// Do this for each mel command your plugin has added into Maya.
	//
	status = plugin.deregisterCommand( "myCommand" ); 
	if (!status) { 
		status.perror("failed to deregister \"myCommand\"\n"); 
		return status; 
	} 
	return status; 
 } 

 

 

 

The Makefile

Now, we should now be able to compile and execute this plugin, the makefile for this plugin is listed below. You will notice that they use two scripts, mayacc instead of g++ -c, and mayald instead of g++ -o. These two scripts simply set the relavant include paths for a maya plugin and make your plugin link to the maya libs (there are about 20 of them!!).

 

 

 


 # Makefile for a simple maya plugin...
 
 # The object files required by this plugin
 OBJECTS= PluginMain.o MyCommand.o
 
 # The output plugin
 OUTPUT= MyCommand.so
 

 all : $(OUTPUT)
 

 # main linking for plugin. Note the use of -shared !!
 $(OUTPUT) : $(OBJECTS)
 	mayald -shared -o $(OUTPUT) $(OBJECTS)


 # compile each cpp file into an object file(*.o)
 PluginMain.o : PluginMain.cpp
 	mayacc -c PluginMain.cpp
 
 MyCommand.o : MyCommand.cpp MyCommand.h
 	mayacc -c MyCommand.cpp


 # so we can type "make clean"
 clean :
 	rm $(OBJECTS) $(OUTPUT)
	
 

 

 

 

MArgList Basics

Well so far our function does very little. We really could do with improving this to properly handle function arguments and custom flags. Arguments for a mel command are passed to the function in the form on an MArgList. We can either parse this manually, or we can make use of MSyntax and MArgDatabase to simplify the process greatly (The recommended method!)

To start with we will just look at parsing the arguments manually. This initially seems like the easier method, however as the complexity of your functions increase, this method becomes distinctly non-trivial.

So even though you *can* do this, you probably don't want to ;)

 

 

 


 MStatus MyCommand::doIt( const MArgList& args ) {

	// a default value for our flag
	bool doSelected = false;

	// loop through the command line arguments
	for( int i=0; i < args.length();++i )
	{
		// check for a valid argument
		if( args.asString(i) == "-sl" || args.asString(i) == "-selected" )
		{
			// we need true/false specified after the flag. Make sure we still have 
			// enough arguments remaining...
			//
			if( ++i == args.length() ) {
				MGlobal::displayError("No argument specified for -sl flag");
				return MS::kFailure;
			}
			
			// should be OK to read the argument
			doSelected = args.asBool(i);
		}
	}

	if(doSelected) {
		// perform action on selected items
	}
	else {
		// perform action on all objects
	}

	return MS::kSuccess;
 }

 

 

 

Related Topics

MSelectionList - selection within the Maya API

MSyntax - simplifying argument parsing for your mel functions

Iterating the Maya Scene

Accessing Parenting Information

MPlug - Manually getting and setting node attributes