I thought i would give it a try making a simple squash deformer on a nurbs surface.
Here's what i made so far. Basically i wanted to get an understanding of first compiling a deformer that uses cv data. Here it just accesses cv data and moves all cvs above the world y = 0 line, down by half the total height of the nurbs surface.
before:
after:
I highly recommend David Gould's Maya Complete programming book. His examples on melt node and rolling node were very very very helpful for getting me started with deformers using Maya api.
To get your feet wet i highly recommend first getting a simple example that uses a header file and .cpp file to work. For example see Chad Vernon's hello command program (chadvernon dot com).
So here is the current draft there are 5 files in total
//Algorithm of node files
testNode.cpp //this is the one to look in to see how cvs, .. were actually moved in the "compute"
testNode.h
//Files to do mostly book keeping and communicate with user
testNodeCmd.h
testNodeCmd.cpp //if you use MEL it does stuff similar to connectAttr and what we see with listConnections
testNodeMain.cpp //this one kind of ties all the pieces together
I'm still fairly new to this stuff, but i think separating out headers makes each file a little more simple to debug. A tip for getting includes is to do search of includes by searching for class name in Maya's api examples (example edit .bash_profile with a function that can search a directory for occurence of a name), this way we can see what we need to include when we use stuff.
Hope this is helpful,
cheers,
Nate
Modify at your own risk (please remove space in "< maya" i added space so could show code in html, note I did little error checking, also the id 0x00334 is temporary you may want to change this):
//what i ran to compile this thing on a macbook
g++ -c testNodeCmd.cpp -arch i386 -DMAC_PLUGIN -DOSMac_MachO_ -DBits32_ -m32 -DUNIX -D_BOOL -DOSMac_ -DFUNCPROTO -D_GNU_SOURCE -fPIC -fno-strict-aliasing -DREQUIRE_IOSTREAM -Wno-deprecated -Wall -Wno-multichar -Wno-comment -Wno-sign-compare -funsigned-char -Wno-reorder -fno-gnu-keywords -ftemplate-depth-25 -pthread -Wno-deprecated -fno-gnu-keywords -g -I/Applications/Autodesk/maya2008/devkit/include -o testNodeCmd.o g++ -c testNode.cpp -arch i386 -DMAC_PLUGIN -DOSMac_MachO_ -DBits32_ -m32 -DUNIX -D_BOOL -DOSMac_ -DFUNCPROTO -D_GNU_SOURCE -fPIC -fno-strict-aliasing -DREQUIRE_IOSTREAM -Wno-deprecated -Wall -Wno-multichar -Wno-comment -Wno-sign-compare -funsigned-char -Wno-reorder -fno-gnu-keywords -ftemplate-depth-25 -pthread -Wno-deprecated -fno-gnu-keywords -g -I/Applications/Autodesk/maya2008/devkit/include -o testNode.o g++ testNodeMain.o testNodeCmd.o testNode.o -ldl -shared -L/Applications/Maya.app/Contents/MacOS -lOpenMaya -lFoundation -Wl,-executable_path,/Applications/Maya.app/Contents/MacOS -lFoundation -lOpenMaya -framework OpenGL -o testNode.bundle
//testNode.cpp
//last updated: 09-15-2014 nate -- initial release //Inspired by David Gould's Complete Maya Programming, rolling node and melting node examples #include "testNode.h" #include < maya/MPlug.h> #include < maya/MDataBlock.h> #include < maya/MTypeId.h> #include < maya/MDataHandle.h> #include < maya/MObject.h> #include < maya/MFnNurbsSurfaceData.h> #include < maya/MFnNurbsSurface.h> #include < maya/MPointArray.h> #include < maya/MFnTypedAttribute.h> //for some error printing #include < string.h> #include < maya/MIOStream.h> MTypeId testNode::id( 0x00334); MObject testNode::inputSurface; MObject testNode::outputSurface; //little error checking //because we are using a bigger structure that has lots of other plugs we dont see //we need to be specific about our plugs/attr MStatus testNode::compute( const MPlug& plug, MDataBlock& data) { MStatus stat; if( plug == outputSurface ) { //get handles to all attributes MDataHandle inputSurfaceHnd = data.inputValue( inputSurface, &stat ); if(stat != MS::kSuccess){cerr << "error MDataHandle" << stat << endl; return stat;} MDataHandle outputSurfaceHnd = data.outputValue( outputSurface, &stat ); if(stat != MS::kSuccess){cerr << "error MDataHandle" << stat << endl; return stat;} //using wrong as will probably cause maya to crash MObject inputSurfaceObj = inputSurfaceHnd.asNurbsSurface(); //make a place to store original input surface //nurbs surface need specific function set for its data see MFnGeometryData to find polys etc //im guessing its kind of like beginning of making a nurbs sphere in maya MFnNurbsSurfaceData surfaceDataFn; MObject newSurfaceData = surfaceDataFn.create(&stat); if(stat != MS::kSuccess){cerr << "error making new output surface" << stat << endl; return stat;} //copy original input surface to new output surface MFnNurbsSurface surfaceFn; surfaceFn.copy( inputSurfaceObj, newSurfaceData ); //im guessing its analagous to using hypergraph and connecting create of one nurbssurface into create of a second nurbas surface //we can now deform the copy without affecting our original surfaceFn.setObject( newSurfaceData ); //giving it an mobject //all surface cvs retrieved and stored in an array at once to save time MPointArray pts; //they should be vectors in worldspace i think from origin to location of cv, it might be from pivot center stat = surfaceFn.getCVs(pts); if(stat != MS::kSuccess){cerr<<"Error getting cvs"<< stat<< endl;return stat;} //figure out have height of nurbs by first getting whole distance unsigned int i; double halfDistance; double minHeight = -1, maxHeight = 1, y; for( i=0; i < pts.length(); i++ ) { y = pts[i].y; if( y < minHeight ){ minHeight = y; } if( y > maxHeight ){ maxHeight = y; } } halfDistance = 0.5*(maxHeight - minHeight); //should have our height //go over cvs this time deforming them MVector vec; for( i = 0; i < pts.length(); i++ ){ MPoint &p = pts[i]; //move each point down vertically by half distance if it was above 0 to start if( p.y > 0 ){ p.y -= halfDistance; } //p.y -= halfDistance; }//DONE WITH DEFORMATION //UPDATE with deformation surfaceFn.setCVs(pts); //Nurbssurface has been changed tell Maya this or else get crash surfaceFn.updateSurface(); //set the output surface attribute to the new data outputSurfaceHnd.set(newSurfaceData); //since attribute has now been recomputed tell plug its clean or get crash stat = data.setClean(plug); if( stat != MS::kSuccess ){cerr<<"error cleaning"<< stat<< endl; return stat;} }//done with plug else{ //for any other plugs we didnt make let Maya know to do its thing stat = MS::kUnknownParameter; //very important } return stat; } //little error checking MStatus testNode::initialize() { MStatus stat; MFnTypedAttribute tAttr;//for nurbs surface attribute inputSurface = tAttr.create( "inputSurface", "is", MFnNurbsSurfaceData::kNurbsSurface, &stat ); if( !stat ){stat.perror("error creating -- inputSurface");return stat;} tAttr.setHidden(true);//hide it, by default its visible outputSurface = tAttr.create( "outputSurface", "os", MFnNurbsSurfaceData::kNurbsSurface, &stat ); //since this can be derived from just input dont need to store it tAttr.setStorable(false); tAttr.setHidden(true);//hide it, by default its visible //add attributes to node stat = addAttribute( inputSurface ); if (!stat) {stat.perror("addAttribute");return stat;} stat = addAttribute( outputSurface ); if (!stat) {stat.perror("addAttribute");return stat;} //the input surface affects output surface this is required or get crash attributeAffects( inputSurface, outputSurface); return MS::kSuccess; }//testNode.h
#ifndef TESTNODE_H #define TESTNODE_H #include < maya/MObject.h> #include < maya/MGlobal.h> #include < maya/MPxNode.h> #include < maya/MPlug.h> #include < maya/MDataBlock.h> #include < maya/MTypeId.h> //Inspired by David Gould RollingNode and Melt Node example //move when deformer applied it translates all cvs above 0 in y by half height class testNode : public MPxNode { public: virtual MStatus compute( const MPlug& plug, MDataBlock& data); static void *creator(){return new testNode();} static MStatus initialize(); //attributes static MObject inputSurface; static MObject outputSurface; //id static MTypeId id; }; #endif//testNodeCmd.cpp
#include "testNodeCmd.h" #include "testNode.h" #include < maya/MItSelectionList.h> #include < maya/MPlugArray.h> #include < maya/MPlug.h> #include < maya/MFnDependencyNode.h> MStatus testNodeCmd::doIt( const MArgList &args ){ MStatus stat; MSelectionList selection; MGlobal::getActiveSelectionList( selection ); //so can apply deformer to any number of selected nurbs MItSelectionList iter( selection, MFn::kNurbsSurface ); for( ; !iter.isDone(); iter.next() ) { MObject shapeNode; //get shape iter.getDependNode(shapeNode); //get plug to create attribute on nurbs shape MFnDependencyNode shapeFn( shapeNode ); MPlug createPlug = shapeFn.findPlug( "create" );//need correct name here //like listconnections to create attribute MPlugArray srcPlugs; createPlug.connectedTo( srcPlugs, true, false ); MPlug srcPlug = srcPlugs[0];//assuming only one input connection to create attribute MObject testNode = dgMod.createNode( testNode::id );//it will need header of testnode //create plugs to input and output surface MFnDependencyNode testFn( testNode ); MPlug outputSurfacePlug = testFn.findPlug("outputSurface"); MPlug inputSurfacePlug = testFn.findPlug("inputSurface"); //before new connection to break existing connection //kind of like getting mel erro attribute locked etc dgMod.disconnect(srcPlug, createPlug); //HOW TO PRINT DATA TYPE OF PLUG //MString outputSurfacePlugInfo = outputSurfacePlug.info(); //MString inputSurfacePlugInfo = inputSurfacePlug.info(); //MString createPlugInfo = createPlug.info(); //MGlobal::displayInfo(createPlugInfo); //MGlobal::displayInfo(outputSurfacePlugInfo); // MGlobal::displayInfo(inputSurfacePlugInfo); dgMod.connect( srcPlug, inputSurfacePlug);//important deformer input surface comes from what created our nurbs dgMod.connect( outputSurfacePlug, createPlug);//important output surface goes into create plug // //automatically assign new node a unique name static int i = 0; MString name = MString("GreatDay_testNode") + i++; dgMod.renameNode( testNode, name ); //if needed to run any mel do it here } //finally call redoit to do the real work return redoIt(); } MStatus testNodeCmd::redoIt() { return dgMod.doIt();//undoing handled by MDGModifier } MStatus testNodeCmd::undoIt() { return dgMod.undoIt(); }//testNodeCmd.h
#ifndef TESTNODECMD_H #define TESTNODECMD_H #include < maya/MArgList.h> #include < maya/MObject.h> #include < maya/MGlobal.h> #include < maya/MPxCommand.h> #include < maya/MDGModifier.h> class testNodeCmd : public MPxCommand { public: virtual MStatus doIt ( const MArgList& ); virtual MStatus undoIt(); virtual MStatus redoIt(); virtual bool isUndoable() const{return true; } static void *creator() {return new testNodeCmd; } //static MSyntax newSyntax(); private: MDGModifier dgMod; }; #endif//testNodeMain.cpp
#include "testNodeCmd.h" #include "testNode.h" #include < maya/MFnPlugin.h> MStatus initializePlugin(MObject obj) { MStatus stat; MFnPlugin pluginFn(obj, "Nate Test Nodes", "1.0", "Any"); stat = pluginFn.registerCommand( "testNodeCmd", testNodeCmd::creator); if(!stat){stat.perror("error registering command");} //need more stuff in initialize plugin for deformer node //this is need to put blue print of nodes attributes its done once stat = pluginFn.registerNode("testNode", testNode::id, testNode::creator, testNode::initialize ); if(!stat){stat.perror("error registering node");} return stat; } MStatus uninitializePlugin(MObject obj) { MStatus stat; MFnPlugin pluginFn( obj ); stat = pluginFn.deregisterCommand("testNodeCmd"); if(!stat){stat.perror("error deregistering command");} stat = pluginFn.deregisterNode(testNode::id);//needs more than just name if(!stat){stat.perror("error deregistering node");} return stat; }Inspired by David Gould
Inspired by Chad Vernon (chadvernon dot com)