Maya API - Node Inheritance

Thoguht I’d start with something simple =)

I am writting a few new Nodes and would like to consolidate my functionality to a single interface (Class, etc…) and pull that into the child nodes, extend them, etc…

The current issue I am having:

In the parent class, I have made it an MPxLocator Node. Added a few core attributes (for tagging the nodes).

The derrivates seem to compile fine, with different sets of attributes on each. I have worked around the way Maya needs to have the forward declared statics (so I do not get a ton of already defines in VS), BUT…

When I createNode one of the sub-types, all I get is the last compiled node. IOW, no matter what node I try to create, Maya only creates a single type of node, of which I ASSUME it to be the last compiled/linked object because I also assume the values for that last linked class is setting the statics.

Obviously it is possible to create chains of nodes, Maya is ripe with them.

How do you handle the Initializations (propigate upwards? how? They are all statically defined).

And how to you define the statics without overruning each other if the parent is the class defining the statics?

I am sure I am doing something anathema to good Maya API use, and I am sure there is a proper way, but I have found little information or example for what I am attempting.

Any direction, help, or beer drinking on my behalf is appreciated.

Cheers.

As far as I understand Maya API actually hates derivative classes. This is why you have the Proxy classes that Maya is so kind to limit you to to derive from.

Ah joy.

Well, I have a “way” to make it work, but I am not sure I want to rest my feet on something not nessicarily vetted against their API.

Cheers

The API shouldn’t care too much about using nodes that were derived from another. I think the thing to keep in mind is that when you’re creating a derived node, you only want to use the code from the base node and not actually create a base node in the scene.

I put this together pretty quick and it seems to work nicely:

pluginMain.cpp


#include <maya/MFnPlugin.h>

#include "Nodes.h"

MStatus initializePlugin( MObject obj )
{
	MStatus status;

	MFnPlugin fnPlugin( obj );
	status = fnPlugin.registerNode( "baseNode", BaseNode::id, BaseNode::creator, BaseNode::initialize );
	status = fnPlugin.registerNode( "derivedNode", DerivedNode::id, DerivedNode::creator, DerivedNode::initialize );

	return status;
}

MStatus uninitializePlugin( MObject obj )
{
	MStatus status;

	MFnPlugin fnPlugin( obj );
	status = fnPlugin.deregisterNode( BaseNode::id );
	status = fnPlugin.deregisterNode( DerivedNode::id );

	return status;
}

Nodes.h


#ifndef __NODES_H__
#define __NODES_H__

#include <maya/MPxNode.h>

class BaseNode : public MPxNode
{
public:
	static void* creator();
	static MStatus initialize();

	static MTypeId id;

	MStatus compute( const MPlug &plug, MDataBlock &dataBlock );

protected:
	static MObject baseAttr;
};


class DerivedNode : public BaseNode
{
public:
	static void* creator();
	static MStatus initialize();

	static MTypeId id;

	MStatus compute( const MPlug &plug, MDataBlock &dataBlock );

protected:
	static MObject derivedAttr;
};


#endif /* __NODES_H__ */

Nodes.cpp


#include "Nodes.h"

#include <maya/MFnNumericAttribute.h>
#include <maya/MGlobal.h>

/*
BaseNode
*/
MTypeId BaseNode::id(0x0);

MObject BaseNode::baseAttr;

void* BaseNode::creator()
{
	return new BaseNode;
}

MStatus BaseNode::initialize()
{
	MStatus status;

	MFnNumericAttribute nAttr;

	baseAttr = nAttr.create( "baseAttr", "baseAttr", MFnNumericData::kDouble, 0, &status );
	if(!status )
		return status;

	nAttr.setKeyable(true);

	addAttribute( baseAttr );

	return status;
}

MStatus BaseNode::compute( const MPlug &plug, MDataBlock &dataBlock )
{
	MStatus status;

	if( plug == baseAttr )
	{
		MGlobal::displayInfo( "Updating baseAttr" );
	}
	else
	{
		return MStatus::kInvalidParameter;
	}

	return status;
}

/*
DerivedNode
*/
MTypeId DerivedNode::id(0x1);

MObject DerivedNode::derivedAttr;

void* DerivedNode::creator()
{
	return new DerivedNode;
}

MStatus DerivedNode::initialize()
{
	MStatus status;

	status = BaseNode::initialize();
	if( !status )
		return status;

	MFnNumericAttribute nAttr;

	derivedAttr = nAttr.create( "derivedAttr", "derivedAttr", MFnNumericData::kDouble, 0, &status );
	if(!status )
		return status;

	nAttr.setKeyable(true);

	addAttribute( derivedAttr );

	return status;
}

MStatus DerivedNode::compute( const MPlug &plug, MDataBlock &dataBlock )
{
	MStatus status;

	// First, let the base class try to process the plug
	status = BaseNode::compute( plug, dataBlock );

	// Only continue beyond this point if the plug passed in
	// was not processed by the base class
	if( status != MStatus::kInvalidParameter )
		return status;

	if( plug == derivedAttr )
	{
		MGlobal::displayInfo( "Updating derivedAttr" );
	}
	else
	{
		return MStatus::kInvalidParameter;
	}

	return status;
}

[QUOTE=senkusha;8763]The API shouldn’t care too much about using nodes that were derived from another. I think the thing to keep in mind is that when you’re creating a derived node, you only want to use the code from the base node and not actually create a base node in the scene.

I put this together pretty quick and it seems to work nicely:
[/QUOTE]

Thanks!

That is more or less what I have currently working with a few exceptions.

The Type IDs and the initialize functions for the nodes themselves.

Because the initializers are statics, if you override them in the children nodes, you wont get them in the parent nodes.

So I have moved all the init code to the post-constructor function.

The type IDs were also being a little wonky, looked like the derrivative nodes were over-running them with the last value of whatever node got compiled.

So I have simply made them static const functions that return the value directly, and that seems to have made them concrete values for each node.

The current chain does a good job on firing the constructors in the order it needs:

basenode::initialize
derrivedNode::postConstructor

However =) I would love to extend this to children of children.

The goal I am trying to achive is something like…

baseNode (with a tagging system inherant) --> controlNode --> armControlNode

for example.

I am not sure the design is good, just blasting a first pass on this.

Cheers for the help

Holy moly.

Yeah, Im an idiot. It was much simpler to do.

After re-looking at Senkusha’s example again I was able to see I was making it more difficult than it needed to be.

Thanks for the assistance.

It may be that I’m an idiot as well, but I’m having the same issue. I have one base class (which is not even registered as a plugin in my pluginMain.cpp), and I have two derived classes, both of which share only the attributes from the base class (neither require additional properties). I’ve tried a few different approaches including custom accessors for the attributes defined statically as well as virtual as well as regular members in the base class, and I’ve also tried retrieving the attribute values via both MPlug and MDataHandler (like normal) in the derived classes, but only one of them will function properly at a time. It seems to have to do with the order in which I register the nodes more than anything else.

Any other insights as to why I might be experiencing this behavior? I’m happy to post additional details/code if it will assist in figuring this out.

TIA!

I’m interested to hear how you worked this out because i dont believe Senkusha’s example will work. There is only one static MObject baseAttr for both the base class and the derived class to share. According to the Maya API documentation:

The create method needs to be called on a per node basis. That means if you want to create and add the same attribute to multiple nodes, you need to call the create method for each node to get a unique MObject back. If you call create just once and add the attribute to multiple nodes, Maya will encounter a fatal error.

Senkusha’s code would overwrite the BaseNode::baseAttr field when the second plugin is registered. I’m fairly certain this is what is happening to me right now, and I haven’t come up with a clean solution for how to solve it just yet.

My first inclination was to use a virtual get/set accessor function for each static field, but that is kindof ugly, and it may not work. Maybe this can be solved with C++ templates?

Thanks

That quote from the documentation is exactly what’s happening here. Look at the order of the calls:


Register DerivedNode
	DerivedNode:initialize
		BaseNode::initialize
			Create baseAttr
			Add baseAttr (to DerivedNode)
		Create derivedAttr
		Add derivedAttr (to DerivedNode)

Register DerivedNode2
	DerivedNode2:initialize
		BaseNode::initialize
			Create baseAttr
			Add baseAttr (to DerivedNode2)
		Create derivedAttr
		Add derivedAttr (to DerivedNode2)

At the end, each derived node has its own copy of baseAttr and derivedAttr. Does that make sense?

But in c++, it is my understanding that a static member of a base class is the same for all of the derived classes- so if the static initialize method calls the base class to create and add a static field, and then a second class calls the same base class, the value from the first class’ call will be overwritten. I’ve attempted a few different test cases outside of the maya api for simplicity: you can run it online from http://codepad.org/E7POIch7 also, i’ve been toying with the idea of templates, but i think that just overcomplicates everything (potentially). http://codepad.org/8ohPpRlF

Ultimately, this could just be a misunderstanding on my part, but i’ve had a tough time proving anything concrete with tests.

Tomorrow I’ll give your code a shot verbatim and see what the result is, then I’ll try it with multiple children under the base class.

Thanks for all of your help!

For reference, please see this article on C++ static member inheritance. http://stackoverflow.com/questions/998247/are-static-members-inherited-c

You ever get around to vetting this? Seems my knee jerk reaction may be bitting me in the arse now.

The behavior is in fact exactly what you thought. It would seem the attrs of the sub-classes are over-writing the base class attrs on subsequent creates / initializes of the sub-classes.

This is way no bueno.

So I am back to the same question: What is the best way to do a large-ish tree of inherited nodes in the Maya API?

I’d really dislike the idea of putting all the attrs and all the code from every base class inside each child and maintain it all. That just seems A) stupid, B) stupid.

Cheers.

You pegged the problem in a previous post:

Because the initializers are statics, if you override them in the children nodes, you wont get them in the parent nodes.

So here’s my question. Why name the initializer functions the same thing?

Name them each something different. There’s not enforcement for the name of the function. The initializer only gets called once per class, on plugin registration, and you pass the function to the initializer. You can call them whatever you want.

[QUOTE=Bronwen;10515]You pegged the problem in a previous post:

So here’s my question. Why name the initializer functions the same thing?

Name them each something different. There’s not enforcement for the name of the function. The initializer only gets called once per class, on plugin registration, and you pass the function to the initializer. You can call them whatever you want.[/QUOTE]

I am fuzzy.

Do you mean why bother naming the initializers all initialize so that when I call a sub-child, that calls the super, that calls yet another super, they call a static, but a differently named static?

If the initializer (initialize) is the entry point for Maya to make the attributes, seperating them and calling the portion that makes the attributes from another function during the initialize callback amounts to the same thing? Yeah I am probably not grokking again.

Not that it is necessarily the proper way, but it obviously work(s)(ed) in Maya API books by David Gould for the example when the book was published (volume II btw pgs. 340+), and not much has changed node wise in Maya?

He does call each super class initialize from the children (forgive the formatting – PDF version copy-paste):


File: CmpMeshModi fi erNode, h
class CmpMeshModifierNode : public MPxNode
{
public:
    static Mstatus initialize() ;
protected:
    static MObject inMesh;
    static MObject outMesh;
};

File: CmpMeshModifierNode, cpp

MObject CmpMeshModifierNode''inMesh"
MObject CmpMeshModifierNode''outMesh"
MStatus CmpMeshModifierNode::initialize()
{
    MFnTypedAttribute tAttr;
    inMesh = tAttr.create( "inMesh", "im", MFnData::kMesh );
    outMesh = tAttr.create( "outMesh", "om", MFnData--kMesh )-
    tAttr.setStorable( false )"
    addAttribute( inMesh )-
    addAttribute( outMesh ).
    attributeAffects( inMesh, outMesh ).
     return MS::kSuccess;
}


Plug-in: Di spl aceMesh
File: Di spl aceMeshNode.h
class DisplaceMeshNode : public CmpMeshModifierNode
{
public:
    virtual MStatus compute( const MPlug &plug, MDataBlock &data );
    static void *creator( ) ;
    static MStatus initialize( ) ;
    static MTypeld id ;
    static MObject strength ;
    static MObject imageFilename;
    static double strengthDefault();
    static MString imageFilenameDefault();
};


Plug-in: oi spl aceMesh
File: Di spl aceMeshNode, cpp

MTypeld DisplaceMeshNode::id( 0x00338 );
MObject DisplaceMeshNode::strength;
MObject DisplaceMeshNode::imageFilename;

void *DisplaceMeshNode::creator()
{
return new DisplaceMeshNode();
}
MStatus DisplaceMeshNode::initialize()
{
    CmpMeshModifierNode::initialize( ) ;
    MFnNumericAttribute nAttr ;
    strength = nAttr.create ( "strength" , "str" , MFnNumericData::kDouble, 1.0 );
    nAttr.setKeyable( true );
    MFnTypedAttribute tAttr ;
    imageFilename = tAttr.create( "imageFilename", "img",MFnData: :kString );
    addAttribute( strength );
    addAttribute( imageFilename );
    attributeAffects ( strength, outMesh );
    attributeAffects ( imageFilename, outMesh );
    return MS::kSuccess;
}

OH, you’re re-defining the inititalize, which is of course static. Yeesh. This is what I get for posting in a haze of adrenaline right after a bulldog attacked my greyhound. Don’t worry, my head’s on straight today, now that my hound is stitched up and mostly back to normal.

So in that example you posted, where the author is calling the superclass initialize from the subclass initialize… that work is unnecessary. The initialize is static and only operates at the class level. Calling the superclass initialize a second time is not going to affect the subclass at all, and only re-does what you’ve already done.

I wish both your threads were in the same thread. I’ve got to go back to the whole static redefine thing.

In your other thread, you were having trouble because you were attempting to treat a re-defined static attribute in the subclass as a polymorphic member from the superclass.

Let’s go back there now.