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