MSelectionList - Iterating Selected Components

 

 

 

So far we have only seen how to select entire nodes, but what about components such as vertices, faces etc ?

Well, to start with lets have a look at the most generalised form of component iteration...

 

 

 



 // 
MSelectionList selList; // get a list of the selected items
MGlobal::getActiveSelectionList( selList ); // we will use an iterator this time to walk over the selection list.
MItSelectionList it( selList ); while( !it.isDone() ) {
MDagPath dagPath; // will hold a path to the selected object
MObject component; // will hold a list of selected components
// we retrieve a dag path to a transform or shape, and an MObject
// to any components that are selected on that object (if any).
//

it.getDagPath( dagPath, component ); // attach a function set to the object
MFnDependencyNode fn(dagPath.node());

// print the object name

std::cout << "\nOBJECT: " << fn.name().asChar() << std::endl; // if the component is list is valid
if (!component.isNull()) { // attach a geometry iterator to the component list. This is the
// most general of the iterators and treats all component types
// such as faces, edges, etc as just a list of points.
//

MItGeometry itGeom( dagPath, component, &stat ); // if OK, if it failed it may just be incompatable? Never seen this happen
if(stat == MS::kSuccess)
{
// loop through all points

while ( !itGeom.isDone() )
{
// get the world space position

MPoint point = itGeom.position(MSpace::kWorld ); // write the index and the position
std::cout << "\t" << itGeom.index()
<< ") " << point.x
<< " " << point.y
<< " " << point.z << "\n"; // next bit of geometry please
itGeom.next();
}
}
} // next selected item
it.next();
}

 

 

 

The main difference between the previous example of walking the selection list is that this time we query both a path to the object, and a list of the components selected.

Using the most generalised of the geometry iterators, MItGeometry, we can walk through the points on any surface, curve, mesh or lattice. If the components say contained a list of polygonal edges, the MItGeometry iterator would walk through the points that made up those edges.

So, ok it's nice to be able to walk through a set of points on an object, but what about things such as edges, faces, isoparms etc?

Well, the answer is a mixed err.... Some iterators exist within Maya to walk over specific component types, these are :

  • MItMeshVertex
  • MItCurveCV
  • MItMeshEdge
  • MItMeshPolygon
  • MItSurfaceCV

I will now hold my hand up and admit that I don't know how to parse any other component types specifically, without using MItGeometry. This is currently very annoying, items such as isoparms would be really nice. Oh well, if anyone does discover how to do this - then please let me know!

So then, onto my best attempt at being able to parse any selected items...

 

 

 



MSelectionList
selList; // Get the list of selected items
MGlobal::getActiveSelectionList( selList ); // we will use an iterator this time to walk over the selection list.
MItSelectionList it( selList );
while ( !it.isDone() ) { MDagPath dagPath;
MObject component;
it.
getDagPath( dagPath, component );
// use a function set to print the name of the object
MFnDependencyNode fn(dagPath.node());
std::cout << "OBJECT: " << fn.name().asChar() << "\n"; // If we have components to iterate over
if (!component.isNull())
{

// determine the component type

switch (component.apiType()) { // we have mesh vertices
case MFn::kMeshVertComponent: { MItMeshVertex itVertex( dagPath, component, &stat );
while ( !itVertex.isDone() )
{
MPoint point = itVertex.position(MSpace::kWorld );

// write the index and the position

std::cout << "\t" << itVertex.index()
<< ") " << point.x
<< " " << point.y
<< " " << point.z << "\n"; itVertex.
next();
}
break;
}
case MFn::kCurveCVComponent: {
MItCurveCV itCurve( dagPath, component, &stat );
while ( !itCurve.isDone() )
{
MPoint point = itCurve.position(MSpace::kWorld );
// write the index and the position

std::cout << "\t" << itVertex.index()
<< ") " << point.x
<< " " << point.y
<< " " << point.z << "\n"; itCurve.
next();
}
break;
}
case MFn::kMeshEdgeComponent: {
MItMeshEdge itEdge( dagPath, component, &stat );
while ( !itEdge.isDone() )
{
MPoint point = itEdge.center(MSpace::kWorld );
// write the index and the position

std::cout << "\t" << itEdge.index()
<< ") " << point.x
<< " " << point.y
<< " " << point.z
<< (itEdge.
isSmooth() ? " smooth\n" : " hard\n"); itEdge.next();
}
break;
}
case MFn::kMeshPolygonComponent: {
MItMeshPolygon itPoly( dagPath, component, &stat );
while ( !itPoly.isDone() )
{
MPoint point = itPoly.center(MSpace::kWorld ); // write the index and the position
std::cout << "\t" << itPoly.index()
<< ") " << point.x
<< " " << point.y
<< " " << point.z
<< (itPoly.
isPlanar() ? " planar\n" : " non-planar\n"); itPoly.next();
}
break;
}
case MFn::kSurfaceCVComponent: {
MItSurfaceCV itCV( dagPath, component, &stat );
while ( !itCV.isDone() )
{
MPoint point = itCV.position(MSpace::kWorld ); // write the index and the position
std::cout << "\t" << itCV.index()
<< ") " << point.x
<< " " << point.y
<< " " << point.z << "\n";
itCV.
next();
}
break;
}
/*--------- The other component types ------------
// I just pass these to MItGeometry. If anyone has
// any better ideas, i'm all ears!
//
case MFn::kMeshVtxFaceComponent:
case MFn::kSubdivFaceComponent:
case MFn::kSubdivEdgeComponent:
case MFn::kCurveEPComponent:
case MFn::kCurveKnotComponent:
case MFn::kCurveParamComponent:
case MFn::kIsoparmComponent:
case MFn::kPivotComponent:
case MFn::kSurfaceEPComponent:
case MFn::kSurfaceKnotComponent:
case MFn::kEdgeComponent:
case MFn::kSurfaceRangeComponent:
case MFn::kDecayRegionCapComponent:
case MFn::kDecayRegionComponent:
case MFn::kMeshComponent:
case MFn::kMeshFrEdgeComponent:
case MFn::kOrientationComponent:
case MFn::kSubVertexComponent:
case MFn::kMultiSubVertexComponent:
case MFn::kSetGroupComponent:
case MFn::kDynParticleSetComponent:
case MFn::kSurfaceFaceComponent:
case MFn::kSubdivMapComponent:
case MFn::kMeshMapComponent:
case MFn::kSubdivCVComponent:
case MFn::kLatticeComponent:
break;
------------------------------------------------*/ // do the default
default:
{
MItGeometry itGeom( dagPath, component, &stat );
if(stat != MS::kSuccess)
break; while ( !itGeom.isDone() )
{
MPoint point = itGeom.position(MSpace::kWorld );

// write the index and the position

std::cout << "\t" << itGeom.index()
<< ") " << point.x
<< " " << point.y
<< " " << point.z << "\n";
// next bit of geometry please itGeom.next(); } }
break;
}
}
it.next(); }

 

 

Maya uses the MSelectionList class to manipulate the current (Active in maya speak) selection list. We can query what items are currently selected by using the function MGlobal::getActiveSelectionList(). This will simply provide us with an MSelectionList which we can traverse....

 

 


 // get a list of the currently selected items 
 MSelectionList selected;
 MGlobal::getActiveSelectionList(selected);

 // iterate through the list of items returned
 for( int i=0; i<selected.length(); ++i )
 {
	MObject obj;
	
	// returns the i'th selected dependency node
	selected.getDependNode(i,obj);
	
	// Attach a function set to the selected object
	MFnDependencyNode fn(obj);

	// write the object name to the script editor
	MGlobal::displayInfo( fn.name().asChar() );
 }

 

 

 

So then, selection may all seem fairly straight forward, however there is an unfortunate subtlety. Within maya, when you select a mesh, you are infact selecting it's transform.

The problem we have then is that we are unlikely to retrieve say a selection list containing transforms. Therefore, we should check the children of any selected transforms to find any shape objects we are expecting to be selected. ie,

 

 

 


 /// \brief	This function is a simple utility function that retrieves the selected 
 ///		objects of the requested type. 
 /// \param	objects	-	the returned list of objects
 /// \param	type	-	the type of objects you want returned
 ///
 void GetSelected(MObjectArray& objects,MFn::Type type) {

	 // get the current selection list from maya
	 MSelectionList selected;
	 MGlobal::getActiveSelectionList(selected);

	 // iterate through all selected items
	 for( int i=0; i<selected.length(); ++i )
	 {
		MObject obj;
		
		// returns the i'th selected dependency node
		selected.getDependNode(i,obj);

		// if the selected object is of the type we are looking for
		if( obj.hasFn(type) ) {
			objects.append(obj);
		}
		else
		// if the selected object is a transform, check it's kids
		if( obj.hasFn(MFn::kTransform) ) {
		
			MFnTransform fn(obj);
			
			// loop through each child of the transform
			for(int j=0;j< fn.childCount();++j)
			{
				// retrieve the j'th child of the transform node
				MObject child = fn.child(j);
			
				// if the child is of the type we are looking for,
				// append it to the list
				if( child.hasFn(type) )
					objects.append(child);
			}
		}
	}
 }

 

 

 

In addition to being able to select individual items, we can also construct a list of items which we can select in one go. For this we simply use the MGlobal::setActiveSelectionList() function.

 

 

 


 /// \brief	this function simply selects all nodes in the scene of the specified type
 /// \param	type	-	the maya type of the nodes to select
 ///
 void SelectItems(MFn::Type type) {

	// get the current selection list from maya
	MSelectionList selected;

	MItDependencyNodes it(type);
	while(!it.isDone())
	{
		selected.add( it.item() );
	
		it.next();
	}

	// set the currently selected items
	MGlobal::setActiveSelectionList(selected);
 }

 

 

 

Related Topics

MPxCommand - Adding Custom mel functions

MSyntax - simplifying argument parsing for your mel functions

Iterating the Maya Scene

Accessing Parenting Information

MPlug - Manually getting and setting node attributes