// Options:
//
// -s The start frame. Default to 1.(double)
// -e The end frame. Default to 60.(double)
// -l length of tail. Default to 5.(double)
// -w width of tail. Default to 2.(double)
// -v variation of tail. Default to 1.(int)
// -px position of tail X. Default to 0.0.(double)
// -py position of tail Y. Default to 0.0.(double)
// -pz position of tail Z. Default to 0.0.(double)
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class tail : public MPxCommand
{
public:
tail();
virtual ~tail();
MStatus doIt( const MArgList& args );
MStatus createBaseCurve();
static void* creator();
private:
double start, end; // frame range
double length, width ; // object option
double posX, posY, posZ ;
int variation ;
};
tail::tail() {}
void* tail::creator()
{
return new tail();
}
tail::~tail()
{
}
MStatus tail::doIt( const MArgList& args )
{
start =3D 1.0;
end =3D 60.0;
length =3D 5.0 ;
width =3D 2.0 ;
variation =3D 1 ;
posX =3D 0.0 ; posY =3D 0.0 ; posZ =3D 0.0 ;
MStatus stat;
double tmp;
unsigned i;
// Parse the arguments.
//
for ( i =3D 0; i < args.length(); i++ )
{
if ( MString( "-s" ) =3D=3D args.asString( i, &stat ) &&
MS::kSuccess =3D=3D stat)
{
tmp =3D args.asDouble( ++i, &stat );
if ( MS::kSuccess =3D=3D stat )
start =3D tmp;
}
else if ( MString( "-e" ) =3D=3D args.asString( i, &stat ) &&
MS::kSuccess =3D=3D stat)
{
tmp =3D args.asDouble( ++i, &stat );
if ( MS::kSuccess =3D=3D stat )
end =3D tmp;
}
else if ( MString( "-l" ) =3D=3D args.asString( i, &stat ) &&
MS::kSuccess =3D=3D stat)
{
tmp =3D args.asDouble( ++i, &stat );
if ( MS::kSuccess =3D=3D stat )
length =3D tmp ;
if( length > (end-start) )
length =3D (end - start) ;
}
else if ( MString( "-w" ) =3D=3D args.asString( i, &stat ) &&
MS::kSuccess =3D=3D stat)
{
tmp =3D args.asDouble( ++i, &stat );
if ( MS::kSuccess =3D=3D stat )
width =3D tmp ;
}
else if ( MString( "-v" ) =3D=3D args.asString( i, &stat ) &&
MS::kSuccess =3D=3D stat)
{
tmp =3D args.asDouble( ++i, &stat );
if ( MS::kSuccess =3D=3D stat )
variation =3D (int)tmp ;
}
else if( MString( "-px" ) =3D=3D args.asString( i, &stat ) &&
MS::kSuccess =3D=3D stat)
{
tmp =3D args.asDouble( ++i, &stat ) ;
if( MS::kSuccess =3D=3D stat )
posX =3D tmp ;
}
else if( MString( "-py" ) =3D=3D args.asString( i, &stat ) &&
MS::kSuccess =3D=3D stat)
{
tmp =3D args.asDouble( ++i, &stat ) ;
if( MS::kSuccess =3D=3D stat )
posY =3D tmp ;
}
else if( MString( "-pz" ) =3D=3D args.asString( i, &stat ) &&
MS::kSuccess =3D=3D stat)
{
tmp =3D args.asDouble( ++i, &stat ) ;
if( MS::kSuccess =3D=3D stat )
posZ =3D tmp ;
}
}
stat =3D createBaseCurve();
return stat;
}
static void makeCurve( const MPointArray& cvs )
{
MStatus stat;
unsigned int deg =3D 1;
MDoubleArray knots;
unsigned int i;
for ( i =3D 0; i < cvs.length(); i++ )
knots.append( (double) i );
// create the curve
MFnNurbsCurve curveFn;
MObject curve =3D curveFn.create( cvs,
knots, deg,
MFnNurbsCurve::kOpen,
false, false,
MObject::kNullObj,
&stat );
if ( MS::kSuccess !=3D stat )
printf("=07Error creating curve.\n");
}
static void makeSurface( const MPointArray& span, const double end, =
const double start,
const double tailLen, const double tailWid, const MPoint movePos )
{
int i, j, n ;
int numCVu =3D span.length() ;
/* Vknot */
const double vKnots[] =3D {
0,0,0,1,1,1
};
MDoubleArray vKnotArray( vKnots, 6 );
/* Uknot */
MDoubleArray uKnotArray( tailLen+2 );
for( n =3D 1, i =3D 3; n < tailLen-3 ; i++, n++ ) {
uKnotArray[i] =3D n;
}
for( ; i < tailLen+2 ; i++ ) {
uKnotArray[i] =3D n;
}
/* CV */
MVectorArray mVecArray( numCVu ) ;
for( i =3D 0 ; i < numCVu-1 ; i++ )
{
mVecArray[i] =3D ( span[i+1] - span[i] ).normal() ;
}
mVecArray[i] =3D mVecArray[i-1] ;
MVectorArray line( 4 ) ;
double d ;
for( i =3D 0 ; i < 4 ; i++ )
{
switch( i )
{
case 0 :
d =3D -tailWid / 2 ;
break ;
case 1 :
d =3D -tailWid / 6 ;
break ;
case 2 :
d =3D tailWid / 6 ;
break ;
case 3 :
d =3D tailWid / 2 ;
break ;
default :
break ;
}
line[i].z =3D d ;
line[i].y =3D 0.0 ;
line[i].x =3D 0.0 ;
}
MMatrix mtx ;
MVector fromVec( MVector::xAxis ), toVec ;
MPointArray posArray ;
for( i =3D 0 ; i < (int)end ; i++ )
{
toVec =3D mVecArray[i] ;
mtx =3D mtx * MQuaternion( fromVec, toVec ).asMatrix() ;
for( j =3D 0 ; j < 4 ; j++ )
{
posArray.append( line[j] * mtx + span[i] + movePos ) ;
}
fromVec =3D toVec ;
}
MPointArray cvArray ;
for( i =3D 0 ; i < (tailLen*4) ; i++ )
{
cvArray.append( posArray[ i ] );
}
/* Make Nurbs Surface */
///////////////////////
MFnNurbsSurface mfnNurbsSurf ;
MObject mSurface ;
MStatus stat ;
mSurface =3D mfnNurbsSurf.create( cvArray, uKnotArray, vKnotArray, 3, 3,
MFnNurbsSurface::kOpen,
MFnNurbsSurface::kClosed,
true, MObject::kNullObj, &stat ) ;
if( MS::kSuccess !=3D stat )
cerr << "make surface failed: status " << stat << endl ;
MFnDependencyNode fnDependNode( mSurface, &stat ) ;
fnDependNode.setName( MString("tailSurface"), &stat ) ;
MGlobal::executeCommand( MString("defaultNavigation -ce -s |") + =
fnDependNode.name()
+ MString(" -d initialShadingGroup ;"), false, false ) ;
/* Set Animation */
//////////////////
double x, y, z, frame ;
int vCnt =3D 0, uCnt =3D 1, cvCnt, add ;
MPlug cvs =3D mfnNurbsSurf.findPlug( "controlPoints", &stat ) ;
if( MS::kSuccess !=3D stat ){
MGlobal::displayError( stat.errorString() ) ;
return ;
}
int cvsNum =3D cvs.numElements() ;
for( cvCnt =3D 0 ; cvCnt < cvsNum ; cvCnt++ )
{
MPlug elem =3D cvs.elementByLogicalIndex( cvCnt ) ;
MPlug Xvalue =3D elem.child(0) ;
MFnAnimCurve acFnSetX ;
acFnSetX.create( Xvalue, NULL, &stat ) ;
if( MS::kSuccess !=3D stat ){
cerr << "Failure creating MFnAnimCurve function set (xValue)\n" ;
continue ;
}
MPlug Yvalue =3D elem.child(1) ;
MFnAnimCurve acFnSetY ;
acFnSetY.create( Yvalue, NULL, &stat ) ;
if( MS::kSuccess !=3D stat ){
cerr << "Failure creating MFnAnimCurve function set (yValue)\n" ;
continue ;
}
MPlug Zvalue =3D elem.child(2) ;
MFnAnimCurve acFnSetZ ;
acFnSetZ.create( Zvalue, NULL, &stat ) ;
if( MS::kSuccess !=3D stat ){
cerr << "Failure creating MFnAnimCurve function set (zValue)\n" ;
continue ;
}
add =3D 0 ;
for( frame =3D start ; frame <=3D end ; frame +=3D 1.0 )
{
if( frame-start < uCnt )
{
x =3D posArray[ vCnt + add ].x ;
y =3D posArray[ vCnt + add ].y ;
z =3D posArray[ vCnt + add ].z ;
add +=3D 4 ;
}
else if( frame-start <=3D tailLen )
{
x =3D posArray[ vCnt + add ].x ;
y =3D posArray[ vCnt + add ].y ;
z =3D posArray[ vCnt + add ].z ;
}
else
{
add +=3D 4 ;
x =3D posArray[ vCnt + add ].x ;
y =3D posArray[ vCnt + add ].y ;
z =3D posArray[ vCnt + add ].z ;
}
MTime tm( frame, MTime::kFilm ) ;
if( ( MS::kSuccess !=3D acFnSetX.addKeyframe( tm, x ) ) ||
( MS::kSuccess !=3D acFnSetY.addKeyframe( tm, y ) ) ||
( MS::kSuccess !=3D acFnSetZ.addKeyframe( tm, z ) ) )
{
cerr << "Error setting the keyframe" << endl ;
}
}
vCnt ++ ;
if( vCnt =3D=3D 4 )
{
vCnt =3D 0 ;
uCnt++ ;
}
}
}
static void makeTube( const MPointArray& span, const double end, const =
double start,
const double tailLen, const double tailWid, const MPoint movePos )
{
int i, j, n ;
int numCVu =3D span.length() ;
/* Vknot */
const double vKnots[] =3D {
0,1,2,3,4,5,6,7,8,9,10,11,12
};
MDoubleArray vKnotArray( vKnots, 13 );
/* Uknot */
MDoubleArray uKnotArray( tailLen+2 );
for( n =3D 1, i =3D 3; n < tailLen-3 ; i++, n++ ) {
uKnotArray[i] =3D n;
}
for( ; i < tailLen+2 ; i++ ) {
uKnotArray[i] =3D n;
}
/* CV */
MVectorArray mVecArray( numCVu ) ;
for( i =3D 0 ; i < numCVu-1 ; i++ )
{
mVecArray[i] =3D ( span[i+1] - span[i] ).normal() ;
}
mVecArray[i] =3D mVecArray[i-1] ;
double pi =3D 3.14159265 ;
double rot =3D 2 * pi / 8.0 ;
MVectorArray circle( 11 ) ;
for( i =3D 0 ; i < 8 ; i++ )
{
circle[i].z =3D cos(rot) * tailWid ;
circle[i].y =3D sin(rot) * tailWid ;
circle[i].x =3D 0.0 ;
rot +=3D 2 * pi / 8.0 ;
}
for( ; i < 11 ; i++ )
{
circle[i].z =3D circle[i-8].z ;
circle[i].y =3D circle[i-8].y ;
circle[i].x =3D 0.0 ;
}
MMatrix mtx ;
MVector fromVec( MVector::xAxis ), toVec ;
MPointArray posArray ;
for( i =3D 0 ; i < (int)end ; i++ )
{
toVec =3D mVecArray[i] ;
mtx =3D mtx * MQuaternion( fromVec, toVec ).asMatrix() ;
for( j =3D 0 ; j < 11 ; j++ )
{
posArray.append( circle[j] * mtx + span[i] + movePos ) ;
}
fromVec =3D toVec ;
}
MPointArray cvArray ;
for( i =3D 0 ; i < tailLen*11 ; i++ )
{
cvArray.append( posArray[ i ] ) ;
}
/* Make Nurbs Tube */
////////////////////
MFnNurbsSurface mfnNurbsSurf ;
MObject mSurface ;
MStatus stat ;
mSurface =3D mfnNurbsSurf.create( cvArray, uKnotArray, vKnotArray, 3, 3,
MFnNurbsSurface::kOpen,
MFnNurbsSurface::kClosed,
true, MObject::kNullObj, &stat ) ;
if( MS::kSuccess !=3D stat )
cerr << "make surface failed: status " << stat << endl ;
MFnDependencyNode fnDependNode( mSurface, &stat ) ;
fnDependNode.setName( MString("tailSurface"), &stat ) ;
MGlobal::executeCommand( MString("defaultNavigation -ce -s |") + =
fnDependNode.name()
+ MString(" -d initialShadingGroup ;"), false, false ) ;
/* Set Animation */
//////////////////
double x, y, z, frame ;
int vCnt =3D 0, uCnt =3D 1, cvCnt, add ;
MPlug cvs =3D mfnNurbsSurf.findPlug( "controlPoints", &stat ) ;
if( MS::kSuccess !=3D stat ){
MGlobal::displayError( stat.errorString() ) ;
return ;
}
int cvsNum =3D cvs.numElements() ;
for( cvCnt =3D 0 ; cvCnt < cvsNum ; cvCnt++ )
{
MPlug elem =3D cvs.elementByLogicalIndex( cvCnt ) ;
MPlug Xvalue =3D elem.child(0) ;
MFnAnimCurve acFnSetX ;
acFnSetX.create( Xvalue, NULL, &stat ) ;
if( MS::kSuccess !=3D stat ){
cerr << "Failure creating MFnAnimCurve function set (xValue)\n" ;
continue ;
}
MPlug Yvalue =3D elem.child(1) ;
MFnAnimCurve acFnSetY ;
acFnSetY.create( Yvalue, NULL, &stat ) ;
if( MS::kSuccess !=3D stat ){
cerr << "Failure creating MFnAnimCurve function set (yValue)\n" ;
continue ;
}
MPlug Zvalue =3D elem.child(2) ;
MFnAnimCurve acFnSetZ ;
acFnSetZ.create( Zvalue, NULL, &stat ) ;
if( MS::kSuccess !=3D stat ){
cerr << "Failure creating MFnAnimCurve function set (zValue)\n" ;
continue ;
}
add =3D 0 ;
for( frame =3D start ; frame <=3D end ; frame +=3D 1.0 )
{
if( frame-start < uCnt )
{
x =3D posArray[ vCnt + add ].x ;
y =3D posArray[ vCnt + add ].y ;
z =3D posArray[ vCnt + add ].z ;
add +=3D 11 ;
}
else if( frame-start <=3D tailLen )
{
x =3D posArray[ vCnt + add ].x ;
y =3D posArray[ vCnt + add ].y ;
z =3D posArray[ vCnt + add ].z ;
}
else
{
add +=3D 11 ;
x =3D posArray[ vCnt + add ].x ;
y =3D posArray[ vCnt + add ].y ;
z =3D posArray[ vCnt + add ].z ;
}
MTime tm( frame, MTime::kFilm ) ;
if( ( MS::kSuccess !=3D acFnSetX.addKeyframe( tm, x ) ) ||
( MS::kSuccess !=3D acFnSetY.addKeyframe( tm, y ) ) ||
( MS::kSuccess !=3D acFnSetZ.addKeyframe( tm, z ) ) )
{
cerr << "Error setting the keyframe" << endl ;
}
}
vCnt ++ ;
if( vCnt =3D=3D 11 )
{
vCnt =3D 0 ;
uCnt++ ;
}
}
}
MStatus tail::createBaseCurve()
{
MStatus stat; // Status code
MDagPathArray picked ;
MDagPath mPath ;
// Create a selection list iterator
//
MSelectionList slist;
MGlobal::getActiveSelectionList( slist );
MItSelectionList iter( slist, MFn::kInvalid,&stat );
// Iterate over all selected dependency nodes
// and save them in a list
//
for ( ; !iter.isDone(); iter.next() )
{
// Get the selected dag path
//
if ( MS::kSuccess !=3D iter.getDagPath( mPath ) )
{
cerr << "Error getting the dag path" << endl;
continue;
}
picked.append( mPath );
}
// array of arrays for object position
MPointArray *pointArrays =3D new MPointArray [ picked.length() * 3 ];
unsigned int i;
double time;
for ( time =3D start; time <=3D end; time++ )
{
MTime timeval(time);
MGlobal::viewFrame( timeval );
// Iterate over selected dag path
//
for ( i =3D 0 ; i < picked.length() ; i++ )
{
mPath =3D picked[i];
MFnTransform fnTransform( mPath ) ;
MVector t =3D fnTransform.translation( MSpace::kWorld, &stat ) ;
#if 0
fprintf( stderr,
"Time =3D %2.2lf, XYZ =3D ( %2.2lf, %2.2lf, %2.2lf )\n\n",
time, t.x, t.y, t.z );
#endif
pointArrays[i].append( MPoint( t ) ) ;
}
}
// make a path curve or surface or tube for each selected object
MPoint movePos( posX, posY, posZ ) ;
for ( i =3D 0 ; i < picked.length() ; i++ )
{
switch( variation )
{
case 0:
makeCurve( pointArrays[i] ) ;
break ;
case 1:
makeSurface( pointArrays[i], end, start, length, width, movePos );
break ;
case 2:
makeTube( pointArrays[i], end, start, length, width, movePos );
break ;
default:
break ;
}
}
delete [] pointArrays;
return MS::kSuccess;
}
MStatus initializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin( obj, "", "3.0", "Any");
status =3D plugin.registerCommand( "tail", tail::creator );
if (!status) {
status.perror("registerCommand");
return status;
}
return status;
}
MStatus uninitializePlugin( MObject obj)
{
MStatus status;
MFnPlugin plugin( obj );
status =3D plugin.deregisterCommand( "tail" );
if (!status) {
status.perror("registerCommand");
return status;
}
return status;
}