- Main Page
- Related Pages
- Modules
- Classes
- Examples

# shellNode.cpp

    //-
    // ==========================================================================
    // Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
    //
    // Use of this software is subject to the terms of the Autodesk
    // license agreement provided at the time of installation or download,
    // or which otherwise accompanies this software in either electronic
    // or hard copy form.
    // ==========================================================================
    //+

    //
    // This plug-in is based, in part, upon the folloing literature:
    //
    //      Digital Seashells, M.B. Cortie,
    //      Computer and Graphics, Vol. 17, No. 1, pp. 79-84, 1993
    //
    //      _Shells_, S. Peter Dance,
    //      Harper Collins Publishers 1992, ISBN 0 7322 0067 9
    //
    //      Modeling Seashels, Deborah R. Fowler, Hans Meinhardt et al.
    //      Siggraph Proceedings 92, pp. 379-387
    //
    //      _The Algorithmic Beauty of Sea Shells_, Hans Meinhardt,
    //      Springer-Verlag Berlin Heidelberg 1995, ISBN 3 540 57842 0
    //

    #include <maya/MPxNode.h>

    #include <maya/MString.h>
    #include <maya/MTypeId.h>
    #include <maya/MPlug.h>
    #include <maya/MDataBlock.h>
    #include <maya/MDataHandle.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MAngle.h>
    #include <maya/MFnUnitAttribute.h>
    #include <maya/MFnNumericAttribute.h>
    #include <maya/MFnTypedAttribute.h>
    #include <maya/MFloatPoint.h>
    #include <maya/MFloatPointArray.h>
    #include <maya/MIntArray.h>
    #include <maya/MDoubleArray.h>

    #include <maya/MFnMesh.h>
    #include <maya/MFnMeshData.h>
    #include <maya/MItMeshVertex.h>

    #include <math.h>
    #include <maya/MIOStream.h>

    #define Rad(x)  ((x)*FPI/180.0F)
    #define Deg(x)  ((x)*180.0F/FPI)
    #define FPI 3.14159265358979323846264338327950288419716939937510582F

    #define McheckErr(stat,msg)         \
        if ( MS::kSuccess != stat ) {   \
            cerr << msg;                \
            return MS::kFailure;        \
        }

    class shellNode : public MPxNode
    {
    public:
        shellNode();
        virtual ~shellNode();

        virtual MStatus         compute( const MPlug& plug, MDataBlock& data );

        static  void *          creator();
        static  MStatus         initialize();

    public:
        static  MTypeId     id;

        // Node attributes
        // ---------------

        // Main shape parameters
        //
        static  MObject alpha;      // Profile (Helico-spiral) param #1
        static  MObject beta;       // Profile (Helico-spiral) param #2
        static  MObject phi;        // Section Starting point
        static  MObject my;         // Section Slant
        static  MObject omega;      // Section angle around Oz
        static  MObject omin;       // Spiral start angle
        static  MObject omax;       // Spiral end angle
        static  MObject od;         // Spiral angle step
        static  MObject smin;       // Section start angle
        static  MObject smax;       // Section end angle
        static  MObject sd;         // Section angle step
        static  MObject A;          // Distance of section from Z-axis
        static  MObject a;          // Section diameter #1
        static  MObject b;          // Section diameter #2
        static  MObject scale;      // Overall scale

        // Nodule 1
        //
        static  MObject P;          // Position on section
        static  MObject L;          // Amplitude (extrusion) of nodule
        static  MObject N;          // Nodules frequency on profile
        static  MObject W1;         // Fatness of nodule
        static  MObject W2;         // Fatness of nodule
        static  MObject nstart;     // Starting point on spiral

        // Nodule 2
        //
        static  MObject P2;
        static  MObject L2;
        static  MObject N2;
        static  MObject W12;
        static  MObject W22;
        static  MObject off2;
        static  MObject nstart2;

        // Nodule 3
        //
        static  MObject P3;
        static  MObject L3;
        static  MObject N3;
        static  MObject W13;
        static  MObject W23;
        static  MObject off3;
        static  MObject nstart3;

        // Ribs
        //
        static  MObject uamp;       // Section rib amplitude
        static  MObject ufreq;      // Section rib frequency
        static  MObject urib;       // Section rib/wave percent
        static  MObject vamp;       // Profile rib amplitude
        static  MObject vfreq;      // Profile rib frequency
        static  MObject vrib;       // Profile rib/wave percent

        // Output mesh
        //
        static  MObject outMesh;

    private:

        struct ShellParams {
            float   alpha;      // Profile (Helico-spiral) param #1
            float   beta;       // Profile (Helico-spiral) param #2
            float   phi;        // Section Starting point
            float   my;         // Section Slant
            float   omega;      // Section angle around Oz
            float   omin;       // Spiral start angle
            float   omax;       // Spiral end angle
            float   od;         // Spiral angle step
            float   smin;       // Section start angle
            float   smax;       // Section end angle
            float   sd;         // Section angle step
            float   A;          // Distance of section from Z-axis
            float   a;          // Section diameter #1
            float   b;          // Section diameter #2
            float   scale;      // Overall scale

            // Nodule 1
            // --------
            float   P;          // Position on section
            float   L;          // Amplitude (extrusion) of nodule
            float   N;          // Nodules frequency on profile
            float   W1;         // Fatness of nodule
            float   W2;         // Fatness of nodule
            float   nstart;     // Starting point on spiral

            // Nodule 2
            // --------
            float   P2;
            float   L2;
            float   N2;
            float   W12;
            float   W22;
            float   off2;
            float   nstart2;

            // Nodule 3
            // --------
            float   P3;
            float   L3;
            float   N3;
            float   W13;
            float   W23;
            float   off3;
            float   nstart3;

            // Ribs
            // ----
            float   uamp;       // Section rib amplitude
            float   ufreq;      // Section rib frequency
            float   urib;       // Section rib/wave percent
            float   vamp;       // Profile rib amplitude
            float   vfreq;      // Profile rib frequency
            float   vrib;       // Profile rib/wave percent
        };

        ShellParams shellParams;
        bool        redoTopology;
        bool        rebuild;

        // Precomputed shell points
        //
        int ni;
        int nj;
        float   **pnts;

    private:

        float GetFloatParameter( MObject node, MObject attr );
        float GetAngleParameter( MObject node, MObject attr );
        static void  addFloatParameter( MObject & attr, MString longName,
                                        MString briefName, float attrDefault );
        static void  addAngleParameter( MObject & attr, MString longName,
                                        MString briefName, float attrDefault );
        void  UpdateParameters();
        void  RedoTopology();
        void  Rebuild();
        float Nodules( float s, float o );
        float Ribs( float u, float v );
        void  Eval( float   *p, float o, float s );

    };

    MTypeId shellNode::id( 0x8000b );

    shellNode::shellNode()
     :  rebuild( true ),
        redoTopology( true ),
        pnts( NULL ),
        ni( 0 ),
        nj( 0 )
    {}

    shellNode::~shellNode() {}

    MStatus shellNode::compute( const MPlug& plug, MDataBlock& data )
    {
        MStatus returnStatus;
        int i, j;

        // Read updated input parameters
        //
        UpdateParameters();
        bool createNewMesh = redoTopology;
        RedoTopology();
        Rebuild();

        if( !pnts || ni<2 || nj<2 ) return MS::kSuccess;

        if (plug == outMesh) {
            if( !pnts || ni<2 || nj<2 ) return MS::kSuccess;

            MDataHandle outputHandle = data.outputValue(outMesh, &returnStatus);
            McheckErr(returnStatus, "ERROR getting polygon data handle\n");
            MObject mesh = outputHandle.asMesh();

            if ( createNewMesh || mesh.isNull() ) {
                MFnMeshData dataCreator;
                MObject newOutputData = dataCreator.create(&returnStatus);
                McheckErr(returnStatus, "ERROR creating outputData");

                MFloatPointArray vertices;

                // Build vertices array
                //
                for( j=0; j<nj; ++j ) {
                    for( i=0; i<ni; ++i ) {
                        const float *p= pnts[j] + 3*i;
                        vertices.append( MFloatPoint(p[0],p[1],p[2]) );
                    }
                }

                // Build poly vertex count array
                //
                MIntArray pcounts;
                i= (nj-1)*(ni-1);
                while( i-- ) {
                    pcounts.append(4);
                }

                // Build poly connectivity array
                //
                MIntArray pconnect;
                for( j=0; j<nj-1; ++j ) {
                    for( i=0; i<ni-1; ++i ) {
                        int corner= i+j*ni;
                        pconnect.append(corner);
                        pconnect.append(corner+1);
                        pconnect.append(corner+1+ni);
                        pconnect.append(corner+ni);
                    }
                }

                // Create some stuff needed by the mesh
                //
                MIntArray       fec;
                MDoubleArray    sp;
                MDoubleArray    tp;

                // Build maya poly object
                //
                MFnMesh meshFn;
                mesh= meshFn.create(
                    nj*ni,              // number of vertices
                    (nj-1)*(ni-1),      // number of polygons
                    vertices,           // The points
                    pcounts,            // # of vertex for each poly
                    pconnect,           // Vertices index for each poly
                    newOutputData,      // Dependency graph data object
                    &returnStatus
                );

                // Update surface
                //
                outputHandle.set(newOutputData);
            } else {
                // The topology hasn't changed, so we can just set the points
                // in the existing mesh
                //
                MItMeshVertex vertIt( mesh, &returnStatus);
                McheckErr(returnStatus, "ERROR creating iterator\n");

                for( j=0; j<nj; ++j ) {
                    for( i=0; i<ni; ++i ) {
                        if ( vertIt.isDone() ) break;
                        const float *p= pnts[j] + 3*i;
                        vertIt.setPosition( MPoint(p[0],p[1],p[2]) );
                        vertIt.next();
                    }
                    if ( vertIt.isDone() ) break;
                }
            }
            data.setClean( plug );
        }

        return MS::kSuccess;
    }

    void* shellNode::creator()
    {
        return new shellNode();
    }

    // Shell Algorithms //

    void shellNode::RedoTopology()
    //
    //  Description:
    //      Adjust our data storage to reflect new parameters
    //
    {
        if( !redoTopology ) return;

        redoTopology = false;

        int oldnj= pnts ? nj : 0;

        ni= 0;
        nj= 0;
        for( float s = shellParams.smin;
             s < shellParams.smax;
             s += shellParams.sd )
        {
            ni++;   // Lazy
        }

        for( float o = shellParams.omin;
             o < shellParams.omax;
             o += shellParams.od )
        {
            nj++;   // Lazy
        }

        if( nj<oldnj ) {
            for( int i = nj; i < oldnj; ++i ) {
                if( pnts[i] ) free( pnts[i] );
            }
        }

        if( nj!=oldnj) {
            pnts = (float**) realloc(pnts, nj*sizeof(float*));
        }

        if( nj>oldnj ) {
            memset( pnts+oldnj, 0, (nj-oldnj)*sizeof(float*) );
        }

        for(int j=0;j<nj;++j) {
            pnts[j] = (float*) realloc(pnts[j],3*ni*sizeof(float));
        }
    }

    void shellNode::Rebuild()
    //
    //  Description:
    //      Rebuild the mesh geometry given the new inputs
    //
    {
        if( !rebuild ) return;
        rebuild= 0;

        int     i;
        int     j;
        float   s;
        float   o;
        float   *p;

        o= shellParams.omin;
        for( j=0; j<nj; ++j )
        {
            s= shellParams.smin;
            p= pnts[j];
            for( i=0; i<ni; ++i )
            {
                Eval( p, o, s );
                s+=shellParams.sd;
                p+=3;
            }
            o+=shellParams.od;
        }
    }

    inline float SafeCot( float x )
    {
        float s= sinf(x);
        return s ? cosf(x)/s : 0.0F;
    }

    inline float G( float a, float n )
    {
        if( !n ) return n;
        float z= 2.0F*FPI;
        a*=n/z;
        return z/n*(a-floorf(0.5F+a));
    }

    float shellNode::Ribs( float u, float v )
    {
        ShellParams & sp = shellParams;

        float zu=0.0F;
        if( sp.uamp )
        {
            zu= sp.uamp*cosf(2.0F*FPI*sp.ufreq*u);
            if( zu<0 ) zu*= (1.0F-2.0F*sp.urib);
        }

        float zv=0.0F;
        if( sp.vamp )
        {
            zv= sp.vamp*cosf(2.0F*FPI*sp.vfreq*v);
            if( zv<0 ) zv*= (1.0F-2.0F*sp.vrib);
        }

        return zu+zv;
    }

    float shellNode::Nodules( float s, float o )
    {
        ShellParams & sp = shellParams;

        float p1;
        float p2;
        float k=0.0F;
        float g=0.0F;

        if( sp.L && sp.N && o>=sp.nstart )
        {
            g= G(o,sp.N);
            p1= g/sp.W2;
            p2= (s-sp.P)/sp.W1;
            k= sp.L*expf( -4.0F*(p1*p1+p2*p2) );
        }

        if( sp.L2 && sp.N2 && o>=sp.nstart2 )
        {
            g= G(o+sp.off2,sp.N2);
            p1= g/sp.W22;
            p2= (s-sp.P2)/sp.W12;
            k+= sp.L2*expf( -4.0F*(p1*p1+p2*p2) );
        }

        if( sp.L3 && sp.N3 && o>= sp.nstart3 )
        {
            g= G(o+sp.off3, sp.N3);
            p1= g/sp.W23;
            p2= (s-sp.P3)/sp.W13;
            k+= sp.L3*expf( -4.0F*(p1*p1+p2*p2) );
        }

        return k;
    }

    void shellNode::Eval( float *p, float o, float s )
    {
        ShellParams & sp = shellParams;

        float ss = sinf(s);
        float cs = cosf(s);
        float re = 1.0F/sqrtf(cs*cs/(sp.a*sp.a)
                   + ss*ss/(sp.b*sp.b));
        float sc = sp.scale*expf(o*SafeCot(sp.alpha));
        float csphi = cosf(s+sp.phi);
        float ssphi = sinf(s+sp.phi);
        float sbeta = sinf(sp.beta);
        float smy = sinf(sp.my);
        float r = re+Nodules(s,o)+Ribs(s,o);

        float x = sp.A*sbeta*cosf(o)
                  + r*csphi*cosf(o+sp.omega)
                  - r*smy*ssphi*sinf(o);

        float y = - sp.A*sbeta*sinf(o)
                  - r*csphi*sinf(o+sp.omega)
                  - r*smy*ssphi*cosf(o);

        float z = - sp.A*cosf(sp.beta)
                  + r*ssphi*cosf(sp.my);

        p[0] =  x*sc;
        p[1] = -z*sc;
        p[2] =  y*sc;
    }

    // Attribute Setup and Maintenance //

        // Main shape parameters
        //
        MObject shellNode::alpha;       // Profile (Helico-spiral) param #1
        MObject shellNode::beta;        // Profile (Helico-spiral) param #2
        MObject shellNode::phi;         // Section Starting point
        MObject shellNode::my;          // Section Slant
        MObject shellNode::omega;       // Section angle around Z
        MObject shellNode::omin;        // Spiral start angle
        MObject shellNode::omax;        // Spiral end angle
        MObject shellNode::od;          // Spiral angle step
        MObject shellNode::smin;        // Section start angle
        MObject shellNode::smax;        // Section end angle
        MObject shellNode::sd;          // Section angle step
        MObject shellNode::A;           // Distance of section from Z-axis
        MObject shellNode::a;           // Section diameter #1
        MObject shellNode::b;           // Section diameter #2
        MObject shellNode::scale;       // Overall scale

        // Nodule 1
        //
        MObject shellNode::P;           // Position on section
        MObject shellNode::L;           // Amplitude (extrusion) of nodule
        MObject shellNode::N;           // Nodules frequency on profile
        MObject shellNode::W1;          // Fatness of nodule
        MObject shellNode::W2;          // Fatness of nodule
        MObject shellNode::nstart;      // Starting point on spiral

        // Nodule 2
        //
        MObject shellNode::P2;
        MObject shellNode::L2;
        MObject shellNode::N2;
        MObject shellNode::W12;
        MObject shellNode::W22;
        MObject shellNode::off2;
        MObject shellNode::nstart2;

        // Nodule 3
        //
        MObject shellNode::P3;
        MObject shellNode::L3;
        MObject shellNode::N3;
        MObject shellNode::W13;
        MObject shellNode::W23;
        MObject shellNode::off3;
        MObject shellNode::nstart3;

        // Ribs
        //
        MObject shellNode::uamp;        // Section rib amplitude
        MObject shellNode::ufreq;       // Section rib frequency
        MObject shellNode::urib;        // Section rib/wave percent
        MObject shellNode::vamp;        // Profile rib amplitude
        MObject shellNode::vfreq;       // Profile rib frequency
        MObject shellNode::vrib;        // Profile rib/wave percent

        // Output mesh
        //
        MObject shellNode::outMesh;

    void shellNode::addFloatParameter( MObject & attr, MString longName,
                                       MString briefName, float attrDefault )
    //
    //  Description:
    //      Add a float input parameter to the node
    //
    {
        MStatus stat;
        MFnNumericAttribute nAttr;
        attr = nAttr.create( longName, briefName, MFnNumericData::kFloat,
                             0.0, &stat );
        if ( stat != MS::kSuccess ) throw stat;

        stat = nAttr.setDefault  ( attrDefault );
        if ( stat != MS::kSuccess ) throw stat;

        stat = nAttr.setKeyable  ( true );
        if ( stat != MS::kSuccess ) throw stat;

        stat = nAttr.setCached   ( true );
        if ( stat != MS::kSuccess ) throw stat;

        stat = nAttr.setStorable ( true );
        if ( stat != MS::kSuccess ) throw stat;

        stat = addAttribute( attr );
        if ( stat != MS::kSuccess ) throw stat;

        stat = attributeAffects( attr, outMesh );
        if ( stat != MS::kSuccess ) throw stat;
    }

    void shellNode::addAngleParameter( MObject & attr, MString longName,
                                       MString briefName, float attrDefault )
    //
    //  Description:
    //      Add an angle input parameter to the node
    //
    {
        MStatus stat;
        MFnUnitAttribute uAttr;

        MAngle defaultAngle( (double)attrDefault, MAngle::kDegrees );

        attr = uAttr.create( longName, briefName, defaultAngle, &stat );
        if ( stat != MS::kSuccess ) throw stat;

        stat = uAttr.setKeyable  ( true );
        if ( stat != MS::kSuccess ) throw stat;

        stat = uAttr.setCached   ( true );
        if ( stat != MS::kSuccess ) throw stat;

        stat = uAttr.setStorable ( true );
        if ( stat != MS::kSuccess ) throw stat;

        stat = addAttribute( attr );
        if ( stat != MS::kSuccess ) throw stat;

        stat = attributeAffects( attr, outMesh );
        if ( stat != MS::kSuccess ) throw stat;
    }

    MStatus shellNode::initialize()
    //
    //  Description
    //      Set up node attributes
    //
    {
        MFnTypedAttribute   typedFn;
        MStatus             stat;

        outMesh = typedFn.create( "outMesh", "o", MFnData::kMesh,
                                  &stat );
        if ( MS::kSuccess != stat ) {
            cerr << "ERROR creating animCube output attribute\n";
            return stat;
        }
        typedFn.setStorable(false);
        typedFn.setWritable(false);
        stat = addAttribute( outMesh );
        McheckErr(stat, "ERROR adding attribute");

        try {
            addAngleParameter( alpha,   "profileParam1",           "pp1",   80.0F );
            addAngleParameter( beta,    "profileParam2",           "pp2",   90.0F );
            addAngleParameter( phi,     "sectionStartingPoint",    "ssp",    1.0F );
            addAngleParameter( my,      "sectionSlant",            "ss",     1.0F );
            addAngleParameter( omega,   "sectionAngleZ",           "saz",    1.0F );
            addAngleParameter( omin,    "spiralStartAngle",        "sps",    0.0F );
            addAngleParameter( omax,    "spiralEndAngle",          "spe", 1200.0F );
            addAngleParameter( od,      "spiralAngleStep",         "spa",    4.0F );
            addAngleParameter( smin,    "sectionStartAngle",       "ssa", -190.0F );
            addAngleParameter( smax,    "sectionEndAngle",         "sea",  190.0F );
            addAngleParameter( sd,      "sectionAngleStep",        "sas",   17.0F );

            addFloatParameter( A,       "distanceFromZ",           "dfz",    1.9F );
            addFloatParameter( a,       "sectionDiameter1",        "sd1",    1.0F );
            addFloatParameter( b,       "sectionDiameter2",        "sd2",    0.9F );
            addFloatParameter( scale,   "scale",                   "s",      0.03F );

            addAngleParameter( P,       "positionOnSection1",      "ps1",   10.0F );
            addFloatParameter( L,       "noduleAmplitude1",        "na1",    1.0F );
            addFloatParameter( N,       "noduleProfileFrequency1", "nf1",   15.0F );
            addAngleParameter( W1,      "noduleFatness11",         "f11",  100.0F );
            addAngleParameter( W2,      "noduleFatness21",         "f21",   20.0F );
            addAngleParameter( nstart,  "spiralStartingPoint1",    "sp1",    0.0F );

            addAngleParameter( P2,      "positionOnSection2",      "ps2",    0.0F );
            addFloatParameter( L2,      "noduleAmplitude2",        "na2",    0.0F );
            addFloatParameter( N2,      "noduleProfileFrequency2", "nf2",    0.0F );
            addAngleParameter( W12,     "noduleFatness12",         "f12",   30.0F );
            addAngleParameter( W22,     "noduleFatness22",         "f22",   30.0F );
            addAngleParameter( off2,    "noduleOffset2",           "no2",    0.0F );
            addAngleParameter( nstart2, "spiralStartingPoint2",    "sp2",    0.0F );

            addAngleParameter( P3,      "positionOnSection3",      "ps3",    0.0F );
            addFloatParameter( L3,      "noduleAmplitude3",        "na3",    0.0F );
            addFloatParameter( N3,      "noduleProfileFrequency3", "nf3",    0.0F );
            addAngleParameter( W13,     "noduleFatness13",         "f13",   30.0F );
            addAngleParameter( W23,     "noduleFatness23",         "f23",   30.0F );
            addAngleParameter( off3,    "noduleOffset3",           "no3",    0.0F );
            addAngleParameter( nstart3, "spiralStartingPoint3",    "sp3",    0.0F );

            addFloatParameter( uamp,    "sectionRibAmplitude",     "sra",    0.0F );
            addFloatParameter( ufreq,   "sectionRibFrequency",     "srf",    0.0F );
            addFloatParameter( urib,    "sectionRibWavePercent",   "srw",    0.0F );
            addFloatParameter( vamp,    "profileRibAmplitude",     "pra",    0.0F );
            addFloatParameter( vfreq,   "profileRibFrequency",     "prf",    0.0F );
            addFloatParameter( vrib,    "profileRibWavePercent",   "prw",    0.0F );
        } catch ( MStatus stat ) {
            fprintf(stderr,"Attribute Initialize Failed\n");
            return stat;
        }

        return MS::kSuccess;
    }

    inline float shellNode::GetFloatParameter( MObject node, MObject attr )
    {
        MPlug plug( node, attr );
        float value;
        plug.getValue( value );
        return value;
    }

    inline float shellNode::GetAngleParameter( MObject node, MObject attr )
    {
        MPlug plug( node, attr );
        MAngle angle;
        plug.getValue( angle );
        return (float)angle.asRadians();
    }

    #define UpdateFloatAttr(ATTR,TOPOLOGY)                          \
        oldValue = shellParams. ATTR;                           \
        shellParams. ATTR = GetFloatParameter( thisObj, ATTR ); \
        if ( shellParams. ATTR != oldValue ) {                  \
            rebuild = true;                                         \
            redoTopology = TOPOLOGY ? true : redoTopology;          \
        }

    #define UpdateAngleAttr(ATTR,TOPOLOGY)                          \
        oldValue = shellParams. ATTR;                           \
        shellParams. ATTR = GetAngleParameter( thisObj, ATTR ); \
        if ( shellParams. ATTR != oldValue ) {                  \
            rebuild = true;                                         \
            redoTopology = TOPOLOGY ? true : redoTopology;          \
        }

    void shellNode::UpdateParameters( )
    //
    //  Description
    //      Read all of the shell parameters and determine what has changed
    //
    {
        MObject thisObj = thisMObject();
        float oldValue;

        UpdateAngleAttr(alpha,false);
        UpdateAngleAttr(beta,false);
        UpdateAngleAttr(phi,false);
        UpdateAngleAttr(my,false);
        UpdateAngleAttr(omega,false);
        UpdateFloatAttr(A,false);
        UpdateFloatAttr(a,false);
        UpdateFloatAttr(b,false);
        UpdateFloatAttr(scale,false);

        UpdateAngleAttr(P,false);
        UpdateFloatAttr(L,false);
        UpdateFloatAttr(N,false);
        UpdateAngleAttr(W1,false);
        UpdateAngleAttr(W2,false);
        UpdateAngleAttr(nstart,false);

        UpdateAngleAttr(P2,false);
        UpdateFloatAttr(L2,false);
        UpdateFloatAttr(N2,false);
        UpdateAngleAttr(W12,false);
        UpdateAngleAttr(W22,false);
        UpdateAngleAttr(off2,false);
        UpdateAngleAttr(nstart2,false);
        UpdateAngleAttr(P3,false);
        UpdateFloatAttr(L3,false);
        UpdateFloatAttr(N3,false);
        UpdateAngleAttr(W13,false);
        UpdateAngleAttr(W23,false);
        UpdateAngleAttr(off3,false);
        UpdateAngleAttr(nstart3,false);
        UpdateFloatAttr(uamp,false);
        UpdateFloatAttr(ufreq,false);
        UpdateFloatAttr(urib,false);
        UpdateFloatAttr(vamp,false);
        UpdateFloatAttr(vfreq,false);
        UpdateFloatAttr(vrib,false);

        // These settings change the topology of the geometry
        //
        UpdateAngleAttr(omin,true);
        UpdateAngleAttr(omax,true);
        UpdateAngleAttr(od,true);
        UpdateAngleAttr(smin,true);
        UpdateAngleAttr(smax,true);
        UpdateAngleAttr(sd,true);
    }

    // Plug-in Initialization //

    MStatus initializePlugin( MObject obj )
    {
        MStatus   status;
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");

        status = plugin.registerNode( "shell", shellNode::id,
                             &shellNode::creator, &shellNode::initialize,
                             MPxNode::kDependNode );
        if (!status) {
            status.perror("registerNode");
            return status;
        }
        return status;
    }

    MStatus uninitializePlugin( MObject obj)
    {
        MStatus   status;
        MFnPlugin plugin( obj );

        status = plugin.deregisterNode( shellNode::id );
        if (!status) {
            status.perror("deregisterNode");
            return status;
        }

        return status;
    }