Custom Locator Nodes

 

,

Adding your own custom locator nodes into maya can be achieved by deriving your own locator node class from the proxy class, MPxLocatorNode. In addition to the standard compute function common to all proxy node types, we can also overide the draw function to provide a real time display method for our locator node type.

You will no doubt be aware by now that locator nodes do not get rendered within our scene. This implies that the draw method we overide is purely a way to provide a real time display of our locator node.

 

 


 // 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/MPxLocatorNode.h>
 #include <maya/MGlobal.h>
 #include <maya/MArgList.h>
 #include <maya/MFnDependencyNode.h>
 #include <maya/MItDependencyNodes.h>

 class MyLocator : public MPxLocatorNode {
 public:

	/// \brief	Renders the real time display of our locator node.
	/// \param	view	-	the viewport we are rendering in
	/// \param	path	-	the dag path to the object
	/// \param	style	-	wireframe, shaded, points etc
	/// \param	status	-	allows us to see if its selected, templated etc
	///
	virtual void draw( M3dView&,
				const MDagPath&,
				M3dView::DisplayStyle,
				M3dView::DisplayStatus ) ;

	/// \brief	This function should return true if the node has a bounding box.
	///			Generally this is needed to enable selection within maya.
	/// \return	true
	///
	virtual void isBounded() const ;

	/// \brief	For internal selection purposes require us to provide maya with a 
	///			rough bounding box for our object.
	/// \return	The bounding box for the locator node
	///
	virtual MBoundingBox boundingBox()const ;

	/// \brief	This function essentially is used to create attributes on a
	/// 		custom node
	/// \return	MS::kSuccess or MS::kFailure
	/// 
	static MStatus initialise() ;

	/// \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;
	}

	/// for identification within maya binary files, we need to provide a typeID for our node 
	static const MTypeId typeId;
	
	/// we also need to provide a type name for the node
	static const MString typeName;
 };

 

,

 

Each new node we add into maya must have its own unique type name and type ID. The type ID allows Maya to identify our object type within a maya binary file.

Any plugins you write should utilize a type ID between the range 0x00000 and 0x7FFFF. If at any point you intend to publish your plugin on the internet, you should approach Alias Wavefront and ask for a unique block of 256 ID numbers. This will ensure that you never have a node ID that clashes with another developers plugin.

In this simple example, the name of the locator will be "myLocator" and it's typeID will be 0x70000 (All of my plugin examples use the id range 0x70000 to 0x70300)

 

 

 


 const MTypeId MyLocator::typeId(0x70000);

 const MString MyLocator::typeName("myLocator");

 

,

 

The next thing we really want to do is register the node with Maya so that it knows how to create our node. This is done in the initializePlugin function. We also must make sure to deregister our node when the plugin gets unloaded.

 

 

 


 #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.registerNode( MyLocator::typeName,
 							MyLocator::typeId, 
							MyLocator::creator ,
							MyLocator::initialize,
							MPxNode::kLocatorNode ); 
	if (!status) 
	{
		status.perror("Failed to register \"myLocator\"\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.deregisterNode( MyLocator::typeId ); 
	if (!status) { 
		status.perror("failed to deregister \"myLocator\"\n"); 
		return status; 
	} 
	return status; 
 } 

 

,

Next we will impliment some basic functionality to our node. We will provide a bounding box for the object, and provide the initialize and creator methods for the node.

Later we will see that the initialize function is used to set up a static description for our node - ie, what attributes it has, if it has any manipulators etc.

 

 


//------------------------------------------------------------------------
/// \brief this function can tell maya whether the locator node has a volume
/// \return true if bounded, false otherwise.
///

bool MyLocator::isBounded() const
{
return true
;
}
//------------------------------------------------------------------------
/// \brief returns the bounding box of the locator node
/// \return the nodes bounding box
///

MBoundingBox MyLocator::boundingBox() const
{
MBoundingBox bbox;
// simply expand the bounding box to contain the points used

bbox.expand( MPoint( -0.5f, 0.0f, -0.5f ) );
bbox.expand( MPoint( 0.5f, 0.0f, -0.5f ) );
bbox.expand( MPoint( 0.5f, 0.0f, 0.5f ) );
bbox.expand( MPoint( -0.5f, 0.0f, 0.5f ) );
bbox.expand( MPoint( 0.0f,-0.5f, 0.0f ) );
bbox.expand( MPoint( 0.0f, 0.5f, 0.0f ) );
return bbox;
}
//------------------------------------------------------------------------
/// \brief this function is called by mata to return a new instance of our locator node
/// \return the new node
///

void* MyLocator::creator()
{
return new
MyLocator;
}
//------------------------------------------------------------------------
/// \brief this function creates a description of our node
/// \return The status code
///

MStatus MyLocator::initialize()
{
return MS
::kSuccess;
}

 

,

Finally we shall provide some openGL routines to handle the realtime display of our node within maya. To do this we impliment our own draw function. We will be provided with the viewport in which we are rendering and a path to our locator node (so we can determine which instance is being rendered).

In addition, the style parameter indicates whether we should render in points, lines, shaded or textured etc. The status parameter indicates whether we need to render this node as selected, templated, selected and templated etc.

The function MPxLocatorNode::colorRGB() allows us to retrieve the colour for the current display status set by the user in the user preferences.

 

 


//------------------------------------------------------------------------
/// \brief This function is used to render our custom locator node
/// \param view - the maya viewport to render the locator node in
/// \param DGpath - the DAG path of the object
/// \param style -
/// \param status -
///
void MyLocator::draw( M3dView& view,
const MDagPath& DGpath,
M3dView::DisplayStyle style,
M3dView::DisplayStatus status )
{
MColor col = colorRGB( status ); // think glPushMatrix()
view.beginGL();
// this makes a copy of the current openGL settings so that anything
// we change will not affect anything else maya draws afterwards.

glPushAttrib( GL_CURRENT_BIT );
// draw a red line

glColor3f(1,0,0);
glBegin(GL_LINES);
glVertex3f( 0.0f,-0.5f, 0.0f );
glVertex3f( 0.0f, 0.5f, 0.0f );
glEnd();
// draw yellow quad on xz plane (going to alpha blend it so we can see through it,
// which i reackon makes it a bit less obtrusive)

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// for some reason, the colour returned for last selected is the same as unselected. // to get around this problem, we simply set it to green ... // if(status == M3dView::kLead)
glColor4f(0,1,0,0.3f);
else
glColor4f(col.r,col.g,col.b,0.3f);
glBegin(GL_QUADS);
glVertex3f( -0.5f, 0.0f, -0.5f );
glVertex3f( 0.5f, 0.0f, -0.5f );
glVertex3f( 0.5f, 0.0f, 0.5f );
glVertex3f( -0.5f, 0.0f, 0.5f );
glEnd();
glDisable(GL_BLEND);
// restore the old openGL settings

glPopAttrib();
// think glPopMatrix()

view.endGL();
}