Maya API: How to smooth a polyObject thats in a dataHandle of a deformerNode?


#1

I am trying to write a little deformer,
Being fairly new to API I am still having trouble understanding the basic fundamentals,

I have a sourceMesh that I am connecting to my deformer node that I would like to use
as a driver. I would like to create a smoothed Mesh out of that sourceMesh, but the way I
have it now, its crashing MAYA.
I seem to be able to “query” and “print” the pointNumber of the sourceObject correctly,
but I can`t print the name / path of the sourceObject. It always returns an empty string.

And as mentioned when I try to generate a smooth object:
smoothMeshTransform = meshFn.generateSmoothMesh();
It crashes my Maya.

My guess is that it has to do with the MObject and the DataHandle.
So I think its data that I am passing around there, and not the actual object.
But my understanding of that is still vague and I don`t know how I can do what I want to
do. (smooth the sourceObject that is connected to the deformer node.)

Nico.

Here is my code, that I have:


//
// File: n_simpleDeform.cc

#include <maya/MPlug.h>
#include <maya/MFnPlugin.h>
#include <maya/MTypeId.h>
#include <maya/MPxDeformerNode.h>
#include <maya/MIOStream.h>
#include <maya/MIOStream.h>
#include <maya/MGlobal.h>

#include <maya/MDagPath.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>

#include <maya/MPoint.h>
#include <maya/MPointArray.h>
#include <maya/MFloatArray.h>

#include <maya/MFnDependencyNode.h>
#include <maya/MFnMesh.h>
#include <maya/MFnMeshData.h>

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

class n_simpleDeform: public MPxDeformerNode
{

public:
n_simpleDeform();
virtual ~n_simpleDeform();

// virtual MStatus compute( const MPlug& plug, MDataBlock& data );
static void* creator();
static MStatus initialize();
virtual MStatus deform ( MDataBlock & data, MItGeometry & iterator, const MMatrix & matrix, unsigned int multiIndex);

         MStatus         convertPolyToSmooth(MObject meshObject, MDagPath &pathToSmooth);    


 static  MTypeId         id;    

 // Inputs
 static MObject          aBind;
 static MObject          aSrcMesh;

private:
unsigned int m_bound, m_boundPrev;

};

// Attributes first time defined
MTypeId n_simpleDeform::id( 0x8104D );
MObject n_simpleDeform::aBind;
MObject n_simpleDeform::aSrcMesh;

MStatus n_simpleDeform::initialize()
{
MFnNumericAttribute nAttr;
MFnTypedAttribute tAttr;

 MStatus status;

// what coming in
aBind = nAttr.create( “bind”, “b”, MFnNumericData::kInt, 0 );
tAttr.setStorable(true);
tAttr.setKeyable(true);
tAttr.setReadable(true);
tAttr.setWritable(true);
tAttr.setCached(false);
status = addAttribute( aBind );

 aSrcMesh = tAttr.create( "srcMesh", "smsh", MFnData::kMesh, MObject::kNullObj, &status );
     tAttr.setStorable(false);
     tAttr.setKeyable(false);
     tAttr.setReadable(true);
     tAttr.setWritable(true);
     tAttr.setCached(false);
     tAttr.setDisconnectBehavior( MFnAttribute::kReset );
     status = addAttribute( aSrcMesh );

// what affects what
attributeAffects( aBind, outputGeom);
attributeAffects( aSrcMesh, outputGeom);

 return MStatus::kSuccess;                

}

//
/* THE DEFORMER PART */
/
/
MStatus n_simpleDeform::deform( MDataBlock& data, MItGeometry & iterator, const MMatrix & matrix, unsigned int multiIndex)
{
MStatus status = MS::kSuccess;
MStatus stat;

//////////////////////////////////////////////////////////////
// Create data handles for input data
/////////////////////////////////////////////////////////////
// input handles
MDataHandle dataHBind = data.inputValue( aBind );
MDataHandle dataHSrcMesh = data.inputValue( aSrcMesh );

 // Variables
 MDagPath            pathToMesh;
 MDagPath            pathToSmoothMesh;



 m_bound            = dataHBind.asInt();
 MObject srcMeshObj = dataHSrcMesh.asMesh();
 if( srcMeshObj == MObject::kNullObj )
     return MS::kSuccess;

 MFnMesh scrMeshFn( srcMeshObj, &status );
 if( status != MS::kSuccess )
     return status;    

 // create a smooth poly from the srcMesh
 convertPolyToSmooth( srcMeshObj, pathToSmoothMesh);



 dataHBind.setClean();
    return status;

}

///////////////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////

MStatus n_simpleDeform::convertPolyToSmooth(MObject meshObject, MDagPath &pathToSmooth)
{
// Variables
MStatus status = MStatus::kSuccess;
MStatus stat;
MObject smoothMeshTransform;

 //1 - First query the scrMesh
     MFnMesh meshFn (meshObject); 

     // print the point number of the srcMesh
     int numOfPoints   = meshFn.numVertices();
     MString txt1 = "ScrMesh has ";
     txt1 += numOfPoints;
     txt1 += " point!";
     MGlobal::displayInfo( txt1 );       

     // print the name of the scrMesh
     MFnDependencyNode nodeFn( meshObject );
     
     MString txt = "The name of the mesh is: ";
     txt += nodeFn.name();
     MGlobal::displayInfo( txt );
     
     // smooth the scrMesh - this is crashing Maya 
         //smoothMeshTransform = meshFn.generateSmoothMesh();          

}

///////////////////////////////////////////////////////////////////////////////////////////
// STANDARD STUFF (initialize and creator, constructor, destructor,…)
///////////////////////////////////////////////////////////////////////////////////////////

/********************************************************************************************/
MStatus initializePlugin( MObject obj ) {
MStatus result;
MFnPlugin plugin( obj, “”, “1.0”, “Any”);
result = plugin.registerNode( “n_simpleDeform”, n_simpleDeform::id, n_simpleDeform::creator,
n_simpleDeform::initialize, MPxNode::kDeformerNode );

 return result;

}

MStatus uninitializePlugin( MObject obj) {
MStatus result;
MFnPlugin plugin( obj );
result = plugin.deregisterNode( n_simpleDeform::id );
return result;
}

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

n_simpleDeform::n_simpleDeform() {

}

n_simpleDeform::~n_simpleDeform() {

}


#2

have you tried
MFnMeshData?

sorry, this is so bold, cause I copied it from the docs.


#3

Hmmm,
but for MFnMeshData I don`t have generateSmoothMesh, do I?
I am not quite sure how I would use that to generate a smooth version of the sourceMesh, then.


#4

No, that s true but it give you a waz to actually access your data, as right now I think that you try to attach MFnMesh to your MObject from the MDataHandle and that might be the cause of failure. I think that the MObject you are getting from the datahandle is a data MObject and not the node itself. Maybe try to print out what kind of MObject you are dealing with something like this:

cout<< "data type " << srcMeshObj.apiTypeStr() << endl;


#5

Your usage of generateSmoothMesh is incorrect.

As Buexe was pointing out, the MObject you receive from the dataHandle is of type MFnMeshData. MFnMesh can accept and operate on this type, but you have to treat it as such rather than as a shape. MFnData types are the data packets that flow through the DG. They do not have a name or path because they are not DAG entities (shape/transforms).

generateSmoothMesh is akin to a different type of MFnMesh::create(). You would use it in a similar manner. In this case, what you want to do populate the arguments (not leave it default) such that the result is parented to your output kMeshData.

So you would want to:

  1. Call MFnMeshData::create(), to create a holder for your new outputMesh, let’s call it outData.
  2. Call generateSmoothMesh with outData as the parentOrOwner parameter.
  3. Then finally set this on your deformer node’s outMesh.
MStatus status;
 MObject outData = MFnMeshData.create();
 MFnMesh meshFn;
 
 // Initialize your meshFn to your input mesh that you want to smooth.
// Let's say you had an input mesh attribute called inMesh.
MDataHandle hInMesh = block.inputValue( aInMesh );
MObject dInMesh = hInMesh.asMesh();  // This call returns MFn::kMeshData type.
meshFn.setObject( dInMesh );
 
 // Now call generateSmoothMesh
 meshFn.generateSmoothMesh( outData, NULL, &status ); 
 
 // outData now holds the smoothed mesh in meshData format
 // Your mistake here is that with parentOrOwner = MObject::kNullObj,
 // meshFn tries to create a new shape without proper setup.
 
 // Now set this value on your outMesh handle
 MDataHandle hOutMesh = block.outputValue( aOutMesh );
 hOutMesh.set( outData );

This is just some pseudocode that you can follow. Fill it in where you need. The key part of this is that MFnMesh operates differently based on the type of data that it was initialized to. It can be initialized to a shape, meshData, meshGeom. Each of them will change how a given function behaves. Some methods don’t work on certain types. These are mostly documented in the API docs.

Hope this helps!


#6

Thanks for the replies!

I tried using your code, Keilun, but when I am trying to compile the plug, I am getting an error:

116: error: expected primary-expression before ‘.’ token

line 116 is this:
MObject outData = MFnMeshData.create();

I am not sure what I am doing wrong! :frowning:

Nico.


#7

My code is just pseudocode. There’s bound to be errors. It was meant to guide you.

Create an MFnMeshData object, then use create:

MFnMeshData meshDataFn;
 MObject outData = meshDataFn.create();

#8

Thanks for the fast reply, Keilun!
I am still in the process of understanding how API works and how to use it.
I have done MEL for a very long time, but API is a completely different beast! :slight_smile:

So this is what I have now for the deform part:

MStatus n_simpleDeform::deform( MDataBlock& data, MItGeometry & iterator, const MMatrix & matrix, unsigned int multiIndex)
{
MStatus status = MS::kSuccess;
MStatus stat;

//////////////////////////////////////////////////////////////
// Create data handles for input data
/////////////////////////////////////////////////////////////

// input handles
MDataHandle dataHBind    = data.inputValue( aBind );   
MDataHandle dataHSrcMesh = data.inputValue( aSrcMesh );

MFnMeshData meshDataFn;
MObject outData = meshDataFn.create();
MFnMesh meshFn;

// Initialize your meshFn to your input mesh that you want to smooth.
MObject dInMesh = dataHSrcMesh.asMesh();  // This call returns MFn::kMeshData type.
meshFn.setObject( dInMesh );

// Now call generateSmoothMesh
meshFn.generateSmoothMesh( outData, NULL, &stat ); 

// dataHBind.setClean();
return status;
}

It now lets me compile,
but having that line
meshFn.generateSmoothMesh( outData, NULL, &stat );
in there still crashes my MAYA :frowning:
Its strange, I have the generateSmoothMesh working in a command plugin,
but I can`t figure out how to port it to a deform node with the
input attr and the dataHandle

Nico.


#9

Hm. I guess contrary to the documentation, it doesn’t work on anything except shapes. Would it be possible to run a smooth operation after the deform, outside of your node?

Eg.

inMesh --> yourDeformer --> polySmoothFace --> outMesh

So basically have your command setup the above connection structure?


#10

Unfortunately not,

What I am ultimately after is a deformer that has a lowRes polyMesh as an input (srcMesh)
which drives / deforms a highRes (poly)-Mesh.

My first approach was to convert the lowRes poly to a subD and then iterate over
deformed Mesh points and find the closest Point and Normal on the sub.
With that I could then calc the offset / distance.
My only problem was that the subD approach did not work due to some
limitation on the maya subDivs.

So someone suggested to use polySmooth inside my deformer instead.
In theory I could smooth the loRes mesh first and then connect it to the deform node
as a scrMesh, but in order to get the cl Points and Normals as accurate as possible
Id have to smooth it at least to level 3 or 4, Having that smoothed mesh floating around all the time, (deform) would make it very slow and heavy, so Id rather have a way to have it in the deformer
and only build when it needs to be calculated (when the loRes polyObject is changed)
and then after the cl points and normals are calced the smoothed mesh would get deleted again.

Hope this makes sense,
now I only have to find a way to do that :slight_smile:


#11

I don’t see how doing it inside the node is any different. From what I’m reading, it sounds like you want it setup like this:

lowResMesh --> polySmoothFace --> deform --> hiResMesh

Note that with this setup, the smoothed mesh isn’t floating around all the time and it will only be calculated when the lowResMesh changes. Am I misunderstanding? Doing this inside your deform node just removes one extra DG node traversal, but is effectively the same thing without having to figure out how to implement the smooth yourself in the deform.


#12

I just tried it out quickly.
I am not sure if I understand you correctly here,
but do you mean you smooth your lowRes mesh
(I did that manually now: from the menu: mesh -> smooth,
and choose 4 divisions)
And then use the output of that as the srcMesh for my deformer,
for calculating the closest Point and Normal on that mesh, then?

It thats the case, I am not sure how the smooth loRes mesh (which is now a
pretty dense highRes mesh) would get deleted. (I don`t have to delete it,
but deforming the lowRes mesh would then get really slow.

(lets say I deform the loRes mesh (=driver) with joints or cluster)
and then smooth it and then give that to my deformer as a scrMesh
to deform my final highRes Geom with it.)

Is that what you meant with
lowResMesh --> polySmoothFace --> deform --> hiResMesh

Or am I missunderstanding something here?
Sorry for having such a hard time understanding what you mean!
(And thanks for your patience with me :-D)


#13

What I’m suggesting is using the DG to do this.

What you what to do if I’m understanding correctly is to:

  1. Start with an input lowRes mesh.
  2. Smooth the lowRes mesh to get an approximate mesh.
  3. Use the smoothed lowRes mesh as an input to your deformer to deform a highRes mesh.

If that’s all correct, then what you want to do is:

  1. Create a polySmoothFace node.
  2. Connect the lowResMesh.outMesh attribute to polySmoothFace.inputPolymesh.
  3. Set the parameters you want on the node. (Eg. set divisions to 4, etc.).
  4. Connect polySmoothFace.output attribute to yourDeformer.srcMesh.

And that’s it. There’s no need to delete objects because now the data is all computed on the fly through the dependency graph. That is what the diagram was supposed to illustrate, a DG network:

lowResMesh --> polySmoothFace --> yourDeformer --> highResMesh.


#14

Yeah, thats what I though you meant.
It would probably be possible to do it that way.
I was kind of hoping, to just create the smooth temporary, to calculate the cl. Points and Normals, and store them along with the “offsets” in arrays in the deformer, and then “destroy” the smooth again.

But writing that down here, makes me now wonder if I would actually gain anything from that,
in terms of speed or memory. I guess you are right, and if I would find a way to do that in
the deformer, it would take as much time and memory as having the smooth “active”
all the time and plug that into the deformer as srcMesh.


#15

You should note that, creating that dependency graph chain is effectively doing what you want to do in the code. There is no cached mesh being held onto at the output of the polySmoothFace.

The dataMesh is only stored on the meshShapes. When an evaluation is requested, the dataMesh from the lowResMesh would be copied, and that copiedMesh is sent to each node along the chain to be operated on until it finally reaches a new meshShape, where it is cached again.

This approach is easier to implement, cleaner and should do what you want (both memory/speed wise).


#16

Now I know what you mean,
with just having the smooth-node but not the actual result in the scene! :slight_smile:

I played a little with it, but haven`t had a chance to test the workflow with my code yet,
my I guess it could work! :slight_smile:

The only thing I found curious was,
I tried to recreate the polySmooth as you would get it with the poly smooth command,
(I first create a lowRes sphere and smoothed it)
Then I created another loRes sphere and a smooth node, and hooked it up in the same way
the first one was connected, but I did not manage to get / see the same smooth result.
I guess that is not possible?! I could probably create something in my code to make it visible
if needed at all. I was just curious.

Anyway, thanks a lot for pointing me into that direction.
Now that I know what you meant, it sounds promising! :slight_smile:


#17

Did you check all the values of the polySmooth node that you manually created to make sure they’re the same? The polySmooth command does set some values on the smooth node after creation/connection.


#18

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.