MFnSet::getMembers Not Working


#1

I am trying to make a code which gives a result of ‘ls -selection -flatten’
It’s supposed to work but I dont know why it is not working.
It gives an error on ‘set.getMembers’ line.
(kFailure): Object does not exist


MSelectionList selList;
MGlobal::getActiveSelectionList(selList);
MObject obj;
selList.getDependNode(0, obj);
MFnSet set(obj);

MSelectionList member;
set.getMembers(member, true);
MStringArray myarray;
member.getSelectionStrings(myarray);
MGlobal::displayInfo(myarray[0]);

Also the python code for same workflow is not working.
It gives an error on ‘om.MFnSet(setObject)’ line.
(kInvalidParameter): Object is incompatible with this method


import maya.OpenMaya as om

mSel = om.MSelectionList()
om.MGlobal.getActiveSelectionList(mSel)
setObject = om.MObject()
mSel.getDependNode(0, setObject)
fnSet = om.MFnSet(setObject)

members = om.MSelectionList()
fnSet.getMembers(members, True)
dagPath = om.MDagPath()
components = om.MObject()
members.getDagPath(0, dagPath, components)

iter = om.MItMeshEdge(dagPath, components)
while not iter.isDone():
    p0 = iter.point(0, om.MSpace.kWorld)
    p1 = iter.point(1, om.MSpace.kWorld)
    print 'edge index:', iter.index(), 'p0:', p0.x, p0.y, p0.z, 'p1:', p1.x, p1.y, p1.z
    iter.next()


#2

It’s hard to tell what your inputs to that code are. Assuming it’s anything in a selection list that a user might select, then I would say your code needs to test the type of the object before it attempts to initialize it with MFnSet.

MFnSet is a function set for set objects and ONLY set objects. If you attempt to initialize MFnSet on a mesh object or a transform, you will get the failure that you’re getting now.

If your goal is to achieve the same functionality as ls -selection -flatten, I don’t think you would need MFnSet unless you’re specifically trying to expand sets. Rather you should be able to achieve this via the MSelectionList.getDagPath(obj, comp)

I would try to retrieve the DAG path first and if it fails, you know it’s a non DAG object so you can use the getDependNode variant instead.

getDagPath returns you two things:

  1. An MObject to the node that was selected.
  2. An MObject to the components that were selected. This will be NULL if no objects were selected.

To achieve the -flatten aspect of the ls command, you would need to determine the type of component that was selected and decompose it into the indices of the components that were selected. For instance, a mesh face is a single indexed component. So you would then do something like this:

MFnSingleIndexedComponent singleCompFn(comp);
MIntArray elements;
singleCompFn.getElements(elements);

From there it should be hopefully be straightforward to list each component index individually.


#3

Thank you so much sir.
I made the first progress with your help.
Now I can manipulate vertices of MfnMesh::getPoints.
There are two things seems to left.
How can I know if user selected a vertex, edge, faces, uvs or something ?
I cant simply convert them to vertices because if user selects an edge, I want to assign similar values to two vertex of this edge or if user selects a face I want to assign similar values to four vertex of this edge.
I am not sure but I guess I can get this by an iterator of MItMeshEdge, MItMeshPolygon, etc.
But the problem is that they are pretty slow on millions of vertices which I want to manipulate, I already get coordinates as fast as possible with MfnMesh::getPoints and assign them as fast as possible with MfnMesh::setPoints.
So I want the solution to be compatible with this route.


#4

I suppose you will need a structure where you keep all mesh data, e.g. an edge struct where you save all edges with their start and end vtx ids and all faces with their vtx ids, normal and uv ids.

With such a structure it should be quite fast to get the vertex ids if you have a single indexed or other components.


#5

Alright, is there a procedure which gives vertex ids of all edges at once ?


#6

So one way you can tackle this is the component MObject that you have will have an apiType() that you can query to determine the type of component selected. eg. MFn::kMeshVertComponent, kMeshEdgeComponent, etc.

Once you have the indices of the selected component and the given type, there are some utility functions in the iterator classes (MItMeshVertex, MItMeshEdge, MItMeshPolygon). These classes have routines that you can use to determine neighboring components. For example, supposed you had an edge index, you could determine its constituent vertices by calling something like this:

// [Input] edgeId  (unsigned int)
MItMeshEdge edgeIt (meshObj);
edgeIt.setIndex(edgeId);
unsigned int vertexId1 = edgeIt.index(0);
unsigned int vertexId2 = edgeIt.index(1);

The same holds true for the other iterators. Like you noted, this isn’t the fastest way to go if you have lots of components to decompose. I would definitely continue to keep your array of points and use the indices to index those arrays directly. At its core, the mesh is composed of a single vertex buffer and then multiple index buffers to denote the mesh structure. There doesn’t appear to be any interfaces on MFnMesh to retrieve/set the array indices that make up the mesh structure, so this is only way I can think of to do it.


#7

Alright then, I will try to handle it as your suggestion, but as you pointed, it will slow down the process because it will regenerate MStatus and check a few same sub-routines for every single edge(which is millions in my case)
Is there somewhere for developers to request features for Autodesk Maya Api ?


#8

May I ask why someone will select millions of edges? I never selected and manipulated millions of edges. Faces, yes and vertices of course, but never edges. In normal workflow you use only a few edges to manipulate a geometry.


#9

It will be for RandomizerUD plugin.
I am planning to add a few advanced noise pattern feature like perlin noise.
So this feature would lead to create formulized base terrain geometries which will be ready for sculpting.
Sometimes these formulas fit better with edges or faces rather than vertices.


#10

I’m not entirely sure I understand why you need to check MStatus all the time. Are there actual error cases you’re looking to handle from your input data?

I suppose another option that isn’t readily exposed to the API is to use the command to convert selection (polyListComponentConversion) from edges to verts and then read back the selection (and later set the selection back to the original edges to keep it transparent).

At the end of the day, worry about making it work first. Then worry about performance. Even if it’s likely the iterator will be your bottleneck, it’s better to optimize based on profile data than assumptions.


#11

Thank you for the answer sir.
I dont need to check MStatus all the time, I was talking about that api function generates MStatus over and over again(even if I dont use or check MStatus) in every single iteration of loops.(which is I believe one of the multiple reasons that makes single functions slower than full functions like getPoint vs getPoints)
I already have some kind of working scripts anyway.
I started to learn C++ Api to convert these scripts to native plugins for getting better performance and flexibility.
polyListComponentConversion seems to be worse than the solution we previously talked about. It would be only a better solution if it had given multi dimensional array at once for multiple selections.
result[0][0] =vtxA //edge 0
result[0][1] =vtxB //edge 0
result[1][0] =vtxC //edge 1
result[1][1] =vtxD //edge 1

so on and so forth