// 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; }