Accessing Nurbs Surface Data [MEL]



 

 

 

Nurbs surfaces in Maya are of type MFn::kNurbsSurface and are usually related to a transform. For example the above hyper-graph view shows the Nurbs Surface "nurbsSphereShape1", it's transform "nurbsSphere1" and the shading group it is connected to, "initialShadingGroup". As with all shapes, there is no obvious connection from the transform to the surface shape.

Maya has a few 'oddities' when it comes to Nurbs. For a start, maya will always return 2 less knots than are actually needed. Secondly periodic and closed surfaces have their edges clamped together. This means only one of the two points will have things like skin weights. This can result in surface tearing unless you are aware of the problem.

 

 

 

 

Finding all Nurbs Surfaces
in a Scene

When retrieving nurbs surface data, you will need to use either MItDependencyNodes or MItDagNode in order to loop through a list of surfaces in the scene.

It is worth querying isIntermediateObject() before exporting any surface. If the function returns true then the node will be part of the scenes history and can be ignored by the exporter.

 

 

 

 

 


#include<maya/MItDag.h>
#include<maya/MFnNurbsSurface.h>

// create an iterator to go through all Nurbs Surface's
MItDag it(MItDag::kDepthFirst,MFn::kNurbsSurface);

while(!it.isDone())
{
 

// attach the function set to the object
MFnNurbsSurface
fn(it.item());

// only want non-history items
if( !fn.isIntermediateObject() ) {

 

// print surface name
cout
<<"Nurb "<< fn.name().asChar() <<endl;

// described in the sections below
outputSurfacePoints
(it.item());
outputSurfaceKnots(it.item());
outputSurfaceInfo(it.item());
OutputNurbsTrimData(it.item());

}

// get next surface
it.next();

}




 

Control Points

 

Vertex co-ordinates are stored in a single array. This array is then later referenced by the faces using a zero based index.

 

To get access to the vertex data, Simply call the getPoints members function of MFnMesh.

 

 

 

 

 

 

 


#include<maya/MFnNurbsSurface.h>
#include<maya/MPointArray.h>

void outputSurfacePoints(MObject& obj)
{
 

// attach the function set to the object
MFnNurbsSurface
fn(obj);

// this will hold the returned control points
MPointArray vts;

// use the function set to get the points

fn.getPoints(vts);

// write number of cvs
cout
<< "NumCVsU " << fn.numCVsInU() << endl;
cout
<< "NumCVsV " << fn.numCVsInV() << endl;

// loop through all points
for(int i=0;i!=vts.length();++i) {

  // print control point
cout << vts[i].x <<" "
<< vts[
i].y <<" "
<< vts[
i].z << "\n";

}

}


 

 

Knot Vectors

 

Maya has some very strange habits when dealing with knot vectors of curves and surfaces.

 

Maya will always return 2 knots less than it should for a given curve. This means that you have to invent the first and last knot for any parametric curve in maya.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


#include<maya/MFnNurbsCurve.h>

void outputSurfaceKnots(MFnNurbsSurface& obj)
{
 

// attach the function set to the object
MFnNurbsSurface
fn(obj);

// write number of knots in the u direction
cout
<< "NumKnotsU "<< (fn.numKnotsInU()+2) << endl;

// write first knot that maya ignores. invent knot
// value for a clamped or periodic curve.
if( (fn.knotInU(1) - fn.knotInU(0)) < 0.01f )

  cout << fn.knotInU(0) << " ";

else

  cout << ( fn.knotInU( 0 ) - 1 ) << " ";

// write rest of the knots
for( int i=0; i<fn.numKnotsInU(); ++i )

  cout << fn.knotInU(i) << " ";

// write last knot (that maya ignores)
if( (fn.knotInU(1) - fn.knotInU(0)) < 0.01f )

  cout<<(fn.knotInU(fn.numKnotsInU()-1))<<"\n";

else

  cout<<(fn.knotInU(fn.numKnotsInU()-1)+1)<<"\n";

// write number of knots in v direction
cout
<< "NumKnotsV"<< (fn.numKnotsInV()+2) << endl;

// write first knot that maya ignores. invent knot
// value for a clamped or periodic curve.
if( (fn.knotInV(1) - fn.knotInV(0)) < 0.01f )

  cout << fn.knotInV(0) << " ";

else

  cout << ( fn.knotInV( 0 ) - 1 ) << " ";

// write rest of the knots
for( int i=0; i<fn.numKnotsInV(); ++i )

  cout << fn.knotInV(i) << " ";

// write last knot (that maya ignores)
if( (fn.knotInV(1) - fn.knotInV(0)) < 0.01f )

  cout<<(fn.knotInV(fn.numKnotsInV()-1))<<"\n";

else

  cout<<(fn.knotInV(fn.numKnotsInV()-1)+1)<<"\n";
}




 

Nurbs Surface Info

A few useful pieces of information may also be required when extracting Nurbs Curve information.

 

The degrees of the surface can be found by using the degreeInU() and the degreeInV() functions of MFnNurbsSurface.

 

The type of the curve used for a given surface direction may also be important. 3 types exist

  • MFnNurbsCurve::kOpen - the curve end and start points do not meet.
  • MFnNurbsCurve::kClosed - the end points of the curve meet, but no continuity remains.
  • MFnNurbsCurve::kPeriodic - the end points of the curves meet and continuity will be maintained.

 

Note, If a surface direction is closed or periodic, then maya simply clamps the last degree number of points to the first degree number of points.

 

ie, if you create a default NURBS cylinder, one surface direction will be a periodic curve of degree 3. essentially the object is a single NURBS patch. Maya automatically clamps the beginning and ending points of the surface to maintain a cylinder shape.

 

This has some major implications when you start looking at skinned Nurbs surfaces. If you are trying to recreate a NURBS surface in your own code, maya will not return any skin weights for the points that it has clamped. Therefore, either duplicate the skinning info for the clamped points, or perform the clamping yourself.

 


#include<maya/MFnNurbsSurface.h>

void outputSurfaceInfo(MObject& obj)
{
 

// attach the function set to the object
MFnNurbsSurface
fn(obj);

// get the curve degree in the U and V directions
cout << "degreeU " << fn.degreeInU() << endl;
cout << "degreeV " << fn.degreeInV() << endl;

// get the curve order in the U and V directions
cout << "orderU " << (fn.degreeInU()+1) << endl;
cout << "orderV " << (fn.degreeInV()+1) << endl;

// write the u surface type
switch(fn.formInU()) {

 

// the curve type is open
case
MFnNurbsSurface::kOpen:
cout << "utype open" << endl;
break;

// the curve type is closed
case
MFnNurbsSurface::kClosed:
cout << "utype closed" << endl;
break;

// the curve type is periodic
case
MFnNurbsSurface::kPeriodic:
cout << "utype periodic" << endl;
break;

// shouldn't get here
default
:
break;

}

// write the v surface type
switch(fn.formInV()) {

 

// the curve type is open
case
MFnNurbsSurface::kOpen:
cout << "vtype open" << endl;
break;

// the curve type is closed
case
MFnNurbsSurface::kClosed:
cout << "vtype closed" << endl;
break;

// the curve type is periodic
case
MFnNurbsSurface::kPeriodic:
cout << "vtype periodic" << endl;
break;

// shouldn't get here
default
:
break;

}

}


 


 

 

Trimmed Surface Data

 

Mayas trimmed surfaces are defined as a set of regions. A region is basically a continuously touching trimmed piece of nurbs surface. The regions are then split into a number of boundaries. Those that define the surface edge are known as outer boundaries. Those that define holes in the surface are known as inner boundaries.

 

Now for the fun part, each boundary it itself composed of a number of curves. You can either query these curves in 3D coordinates or 2D parametric surface coordinates. The code for this is fairly unpleasant, so i have printed the code in full here.

 

 


 

 

void OutputNurbsTrimData(MObject& obj) {

 

cout << "isTrimmed " << fn.isTrimmedSurface() << endl;
// ignore nodes that form part of the history
// and makes ure the nurbs surface is trimmed.

if( fn.isTrimmedSurface() )
{

 

int numRegions = fn.numRegions();
cout << "num_regions " << numRegions << endl;
for(int i=0; i!= numRegions; ++i)
{

 

cout << "Region {\n\tnumBoundaries " << fn.numBoundaries(i) << "\n";
// run througn each boundary on the surface
for( int j=0;j<fn.numBoundaries(i);++j)
{

 

cout << "\tBoundary " << " {\n";
// what type of boundary do we have?
switch(fn.boundaryType(i,j))
{

  case MFnNurbsSurface::kInner:
cout << "\t\tType InnerBoundary_ClockWise\n";
break;

case MFnNurbsSurface::kOuter:
cout << "\t\tType OuterBoundary_AntiClockWise\n";
break;

case MFnNurbsSurface::kSegment:
cout << "\t\tType BoundarySegment_CurveOnFace\n";
break;

case MFnNurbsSurface::kClosedSegment:
cout << "\t\tType ClosedBoundarySegment_ClosedCurveOnFace\n";
break;

default:
break;

}
cout << "\t\tnumCurves " << fn.numEdges(i,j) << endl;
// loop through each 'edge' on a boundary. Basically the edge
// is a curve....
//

for(int k =0; k!=fn.numEdges(i,j); ++k )
{

 


int l;
//
// The last param of the function is whether you want the
// trim curves in 2D parametric surface coords (true) or
// 3D local space coords... for no reason at all i might
// as well grab both ;)
//

MObjectArray CurveObject2D = fn.edge(i,j,k,true);

if (!CurveObject2D.length())
{

  continue;

}
// Attach a function set to the object
//

MFnNurbsCurve fnCurve( CurveObject2D[0] );

cout << "\t\ttrimcurve {\n"
<< "\t\t\t2D_ParamCurve\n"
<< "\t\t\tDegree "
<< fnCurve.degree()
<< "\n\t\t\tKnots "
<< (fnCurve.numKnots()+2)
<< "\n\t\t\t\t";



// mayas annoying knot problem

if( fnCurve.knot(1) - fnCurve.knot(0) < 0.01f )

  cout << fnCurve.knot(0) << " ";

else

  cout << ( fnCurve.knot( 0 ) - 1 ) << " ";


for( l=0;l<fnCurve.numKnots(); ++l )
{

  cout << fnCurve.knot(l) << " ";

}
if( fnCurve.knot(1) - fnCurve.knot(0) < 0.01f )

  cout << (fnCurve.knot(fnCurve.numKnots()-1) ) << "\n";

else

  cout << (fnCurve.knot(fnCurve.numKnots()-1)+1.0f) << "\n";

// the CV's for the 2D curve
cout << "\t\t\tCVS " << fnCurve.numCVs() << "\n";

MPointArray pnts;
fnCurve.
getCVs(pnts);
for( l=0; l<pnts.length(); ++l )
{

  cout << "\n\t\t\t\t"
<< pnts[l].x <<
" "
<< pnts[l].y;

}
cout << "\n";

// Do it all again, but this time for the 3D coords
MObjectArray CurveObject3D = fnSurface.edge(i,j,k,false);

// Attach a function set to the object
//

fnCurve.setObject( CurveObject3D[0] );

cout << "\t\t\t3D_LocalCurve\n"
<< "\t\t\tDegree "
<< fnCurve.degree()
<< "\n\t\t\tKnots "
<< (fnCurve.numKnots()+2)
<< "\n\t\t\t\t";


// mayas annoying knot problem

if( fnCurve.knot(1) - fnCurve.knot(0) < 0.01f )

  cout << fnCurve.knot(0) << " ";

else

  cout << ( fnCurve.knot( 0 ) - 1 ) << " ";

for( l=0;l<fnCurve.numKnots(); ++l )
{

  cout << fnCurve.knot(l) << " ";

}
if( fnCurve.knot(1) - fnCurve.knot(0) < 0.01f )

  cout << (fnCurve.knot(fnCurve.numKnots()-1) ) << "\n";

else
cout << (fnCurve.knot(fnCurve.numKnots()-1)+1.0f) << "\n";

// the CV's for the 3D curve
cout << "\t\t\tCVS " << fnCurve.numCVs() << "\n";
fnCurve.getCVs(pnts);
for(l=0;l<pnts.length();++l)
{

  cout << "\n\t\t\t\t"
<< pnts[l].x << " "
<< pnts[l].y << " "
<< pnts[l].z;

}
cout << "\n";
cout << "\t\t}\n";


}
cout<< "\t}\n";

}

cout << "}\n";

}

}



 

 

What Next?

Material Data

Transformation Data

Blend Shape Deformers

Soft Skinned Nurbs Surfaces

Rigid Skinned Nurbs Surfaces

Lattice Deformers

index

Rob Bateman [2004]


[HOME] [MEL] [API]