helixTool.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.
// ==========================================================================
//+

//
// helixTool.cpp
//
// Description:
//     Interactive tool to draw a helix.
//     Uses OpenGL to draw a guideline for the helix.
//
//
// Steps in creating a tool command
//
// 1. Create a tool command class. 
//    Same process as an MPxCommand except define 2 methods
//    for interactive use: cancel, and finalize.
//    There is also an addition constructor MPxToolCommand(), which 
//    is called from your context when the command needs to be invoked.
//
// 2. Define your context.
//    This is accomplished by deriving off of MPxContext and overriding
//    whatever methods you need.
//
// 3. Create a command class to create your context.
//    You will call this command in Maya to create and name a context.
//
#include <stdio.h>
#include <maya/MIOStream.h>
#include <math.h>

#include <maya/MString.h>
#include <maya/MArgList.h>
#include <maya/MEvent.h>
#include <maya/MGlobal.h>
#include <maya/M3dView.h>
#include <maya/MPoint.h>
#include <maya/MPointArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MDagPath.h>

#include <maya/MPxContext.h>
#include <maya/MPxContextCommand.h>
#include <maya/MPxToolCommand.h> 
#include <maya/MToolsInfo.h>

#include <maya/MFnPlugin.h>
#include <maya/MFnNurbsCurve.h> 

#include <maya/MSyntax.h>
#include <maya/MArgParser.h>
#include <maya/MArgDatabase.h>
#include <maya/MCursor.h>

#if defined(OSMac_MachO_)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif

#define kPitchFlag                      "-p"
#define kPitchFlagLong          "-pitch"
#define kRadiusFlag                     "-r"
#define kRadiusFlagLong         "-radius"
#define kNumberCVsFlag          "-ncv"
#define kNumberCVsFlagLong      "-numCVs"
#define kUpsideDownFlag         "-ud"
#define kUpsideDownFlagLong     "-upsideDown"

// The users tool command

#define         NUMBER_OF_CVS           20

class helixTool : public MPxToolCommand
{
public:
                                        helixTool(); 
        virtual                 ~helixTool(); 
        static void*    creator();

        MStatus                 doIt(const MArgList& args);
        MStatus                 parseArgs(const MArgList& args);
        MStatus                 redoIt();
        MStatus                 undoIt();
        bool                    isUndoable() const;
        MStatus                 finalize();
        static MSyntax  newSyntax();
        
        void                    setRadius(double newRadius);
        void                    setPitch(double newPitch);
        void                    setNumCVs(unsigned newNumCVs);
        void                    setUpsideDown(bool newUpsideDown);

private:
        double                  radius;         // Helix radius
        double                  pitch;          // Helix pitch
        unsigned                numCV;                  // Helix number of CVs
        bool                    upDown;                 // Helix upsideDown
        MDagPath                path;                   // The dag path to the curve.
                                                                        // Don't save the pointer!
};


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

helixTool::~helixTool() {}

helixTool::helixTool()
{
        numCV = 20;
        upDown = false;
        setCommandString("helixToolCmd");
}
        
MSyntax helixTool::newSyntax()
{
        MSyntax syntax;

        syntax.addFlag(kPitchFlag, kPitchFlagLong, MSyntax::kDouble);
        syntax.addFlag(kRadiusFlag, kRadiusFlagLong, MSyntax::kDouble);
        syntax.addFlag(kNumberCVsFlag, kNumberCVsFlagLong, MSyntax::kUnsigned);
        syntax.addFlag(kUpsideDownFlag, kUpsideDownFlagLong, MSyntax::kBoolean);
        
        return syntax;
}

MStatus helixTool::doIt(const MArgList &args)
//
// Description
//     Sets up the helix parameters from arguments passed to the
//     MEL command.
//
{
        MStatus status;

        status = parseArgs(args);

        if (MS::kSuccess != status)
                return status;

        return redoIt();
}

MStatus helixTool::parseArgs(const MArgList &args)
{
        MStatus status;
        MArgDatabase argData(syntax(), args);

        if (argData.isFlagSet(kPitchFlag)) {
                double tmp;
                status = argData.getFlagArgument(kPitchFlag, 0, tmp);
                if (!status) {
                        status.perror("pitch flag parsing failed");
                        return status;
                }
                pitch = tmp;
        }
        
        if (argData.isFlagSet(kRadiusFlag)) {
                double tmp;
                status = argData.getFlagArgument(kRadiusFlag, 0, tmp);
                if (!status) {
                        status.perror("radius flag parsing failed");
                        return status;
                }
                radius = tmp;
        }
        
        if (argData.isFlagSet(kNumberCVsFlag)) {
                unsigned tmp;
                status = argData.getFlagArgument(kNumberCVsFlag, 0, tmp);
                if (!status) {
                        status.perror("numCVs flag parsing failed");
                        return status;
                }
                numCV = tmp;
        }
        
        if (argData.isFlagSet(kUpsideDownFlag)) {
                bool tmp;
                status = argData.getFlagArgument(kUpsideDownFlag, 0, tmp);
                if (!status) {
                        status.perror("upside down flag parsing failed");
                        return status;
                }
                upDown = tmp;
        }

        return MS::kSuccess;
}       


MStatus helixTool::redoIt()
//
// Description
//     This method creates the helix curve from the
//     pitch and radius values
//
{
        MStatus stat;

        const unsigned  deg     = 3;            // Curve Degree
        const unsigned  ncvs    = numCV;                // Number of CVs
        const unsigned  spans   = ncvs - deg;   // Number of spans
        const unsigned  nknots  = spans+2*deg-1;// Number of knots
        unsigned            i;
        MPointArray             controlVertices;
        MDoubleArray    knotSequences;

        int upFactor;
        if (upDown) upFactor = -1;
        else upFactor = 1;

        // Set up cvs and knots for the helix
        //
        for (i = 0; i < ncvs; i++)
                controlVertices.append(MPoint(radius * cos((double) i),
                                                                          upFactor * pitch * (double) i, 
                                                                          radius * sin((double) i)));

        for (i = 0; i < nknots; i++)
                knotSequences.append((double) i);

        // Now create the curve
        //
        MFnNurbsCurve curveFn;

        curveFn.create(controlVertices, knotSequences, deg, 
                                   MFnNurbsCurve::kOpen, false, false, 
                                   MObject::kNullObj, &stat);

        if (!stat) {
                stat.perror("Error creating curve");
                return stat;
        }

        stat = curveFn.getPath( path );

        return stat;
}

MStatus helixTool::undoIt()
//
// Description
//     Removes the helix curve from the model.
//
{
        MStatus stat; 
        MObject transform = path.transform();
        stat = MGlobal::removeFromModel( transform );
        return stat;
}

bool helixTool::isUndoable() const
//
// Description
//     Set this command to be undoable.
//
{
        return true;    
}

MStatus helixTool::finalize()
//
// Description
//     Command is finished, construct a string for the command
//     for journalling.
//
{
        MArgList command;
        command.addArg(commandString());
        command.addArg(MString(kRadiusFlag));
        command.addArg(radius);
        command.addArg(MString(kPitchFlag));
        command.addArg(pitch);
        command.addArg(MString(kNumberCVsFlag));
        command.addArg((int) numCV);
        command.addArg(MString(kUpsideDownFlag));
        command.addArg(upDown);
        return MPxToolCommand::doFinalize( command );
}

void helixTool::setRadius(double newRadius)
{
        radius = newRadius;
}

void helixTool::setPitch(double newPitch)
{
        pitch = newPitch;
}

void helixTool::setNumCVs(unsigned newNumCVs)
{
        numCV = newNumCVs;
}

void helixTool::setUpsideDown(bool newUpsideDown)
{
        upDown = newUpsideDown;
}


//
// The user Context
//
//   Contexts give the user the ability to write functions
//   for handling events.
//
//   Contexts aren't registered in the plugin, instead a
//   command class (MPxContextCommand) is registered and is used
//   to create instances of the context.
//
const char helpString[] = "Click and drag to draw helix";

class helixContext : public MPxContext
{
public:
                                        helixContext();
        virtual void    toolOnSetup(MEvent &event);
        virtual MStatus doPress(MEvent &event);
        virtual MStatus doDrag(MEvent &event);
        virtual MStatus doRelease(MEvent &event);
        virtual MStatus doEnterRegion(MEvent &event);

        virtual void    getClassName(MString & name) const;

        void                    setNumCVs(unsigned newNumCVs);
        void                    setUpsideDown(bool newUpsideDown);
        unsigned                numCVs();
        bool                    upsideDown();

private:
        short                   startPos_x, startPos_y;
        short                   endPos_x, endPos_y;
        unsigned                numCV;
        bool                    upDown;
        M3dView                 view;
        GLdouble                height,radius;
        
};

helixContext::helixContext() 
{
        numCV = 20;
        upDown = false;
        setTitleString("Helix Tool");

        setCursor( MCursor::defaultCursor );

        // Tell the context which XPM to use so the tool can properly
        // be a candidate for the 6th position on the mini-bar.
        setImage("helixTool.xpm", MPxContext::kImage1 );
}

void helixContext::toolOnSetup(MEvent &)
{
        setHelpString(helpString);
}

MStatus helixContext::doPress(MEvent &event)
{
    event.getPosition(startPos_x, startPos_y);
    view = M3dView::active3dView();
    view.beginGL();
    view.beginOverlayDrawing();
    return MS::kSuccess;
}

MStatus helixContext::doDrag(MEvent & event)
{
    event.getPosition(endPos_x, endPos_y);
    view.clearOverlayPlane();
    glIndexi(2);

        int upFactor;
        if (upDown) upFactor = 1;
        else upFactor = -1;

    // Draw the guide cylinder
    //
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
        glRotatef(upFactor*90.0f, 1.0f, 0.0f, 0.0f);
        GLUquadricObj *qobj = gluNewQuadric();
        gluQuadricDrawStyle(qobj, GLU_LINE);
        GLdouble factor = (GLdouble)numCV;
        radius = double(abs(endPos_x - startPos_x))/factor + 0.1;
        height = double(abs(endPos_y - startPos_y))/factor + 0.1;
        gluCylinder( qobj, radius, radius, height, 8, 1 );
    glPopMatrix();

#ifdef _WIN32
        SwapBuffers( view.deviceContext() );
#elif defined (OSMac_)
        // On 64-bit OSX the GL context is an NSOpenGLContext ptr. To swap
        // buffers using that requires Cocoa code, which we're not yet set
        // up to do. So we currently only do the buffer swap on 32-bit OSX.
# if !defined(__x86_64__)
        ::aglSwapBuffers( view.display());   
# endif
#else
        glXSwapBuffers( view.display(), view.window() );
#endif // _WIN32

        return MS::kSuccess;
}

MStatus helixContext::doRelease( MEvent & )
{
    // Clear the overlay plane & restore from overlay drawing
    //
    view.clearOverlayPlane();
    view.endOverlayDrawing();
    view.endGL();

        helixTool * cmd = (helixTool*)newToolCommand();
        cmd->setPitch( height/numCV );
        cmd->setRadius( radius );
        cmd->setNumCVs( numCV );
        cmd->setUpsideDown( upDown );
        cmd->redoIt();
        cmd->finalize();
        return MS::kSuccess;
}

MStatus helixContext::doEnterRegion( MEvent & )
{
        return setHelpString( helpString );
}

void helixContext::getClassName( MString & name ) const
{
        name.set("helix");
}

void helixContext::setNumCVs( unsigned newNumCVs )
{
        numCV = newNumCVs;
        MToolsInfo::setDirtyFlag(*this);
}

void helixContext::setUpsideDown( bool newUpsideDown )
{
        upDown = newUpsideDown;
        MToolsInfo::setDirtyFlag(*this);
}

unsigned helixContext::numCVs()
{
        return numCV;
}

bool helixContext::upsideDown()
{
        return upDown;
}

//
// Context creation command
//
//  This is the command that will be used to create instances
//  of our context.
//


class helixContextCmd : public MPxContextCommand
{
public: 
                                                helixContextCmd();
        virtual MStatus         doEditFlags();
        virtual MStatus         doQueryFlags();
        virtual MPxContext* makeObj();
        virtual MStatus         appendSyntax();
        static void*            creator();

protected:
    helixContext*               fHelixContext;

};

helixContextCmd::helixContextCmd() {}

MPxContext* helixContextCmd::makeObj()
//
// Description
//    When the context command is executed in maya, this method
//    be used to create a context.
//
{
        fHelixContext = new helixContext();
        return fHelixContext;
}

void* helixContextCmd::creator()
//
// Description
//    This method creates the context command.
//
{
        return new helixContextCmd;
}


MStatus helixContextCmd::doEditFlags()
{
        MStatus status = MS::kSuccess;
        
        MArgParser argData = parser();
        
        if (argData.isFlagSet(kNumberCVsFlag)) {
                unsigned numCVs;
                status = argData.getFlagArgument(kNumberCVsFlag, 0, numCVs);
                if (!status) {
                        status.perror("numCVs flag parsing failed.");
                        return status;
                }
                fHelixContext->setNumCVs(numCVs);
        }

        if (argData.isFlagSet(kUpsideDownFlag)) {
                bool upsideDown;
                status = argData.getFlagArgument(kUpsideDownFlag, 0, upsideDown);
                if (!status) {
                        status.perror("upsideDown flag parsing failed.");
                        return status;
                }
                fHelixContext->setUpsideDown(upsideDown);
        }
        
        return MS::kSuccess;
}

MStatus helixContextCmd::doQueryFlags()
{
        MArgParser argData = parser();
        
        if (argData.isFlagSet(kNumberCVsFlag)) {
                setResult((int) fHelixContext->numCVs());
        }
        if (argData.isFlagSet(kUpsideDownFlag)) {
                setResult(fHelixContext->upsideDown());
        }
        
        return MS::kSuccess;
}

MStatus helixContextCmd::appendSyntax()
{
        MSyntax mySyntax = syntax();
        
        if (MS::kSuccess != mySyntax.addFlag(kNumberCVsFlag, kNumberCVsFlagLong,
                                                                                 MSyntax::kUnsigned)) {
                return MS::kFailure;
        }
        if (MS::kSuccess != 
                mySyntax.addFlag(kUpsideDownFlag, kUpsideDownFlagLong,
                                                 MSyntax::kBoolean)) {
                return MS::kFailure;
        }

        return MS::kSuccess;
}

//
// The following routines are used to register/unregister
// the commands we are creating within Maya
//
MStatus initializePlugin( MObject obj )
{
        MStatus status;
        MFnPlugin plugin(obj, PLUGIN_COMPANY, "3.0", "Any");

        // Register the context creation command and the tool command 
        // that the helixContext will use.
        // 
        status = plugin.registerContextCommand("helixToolContext",
                                                                                   helixContextCmd::creator,
                                                                                   "helixToolCmd",
                                                                                   helixTool::creator,
                                                                                   helixTool::newSyntax);
        if (!status) {
                status.perror("registerContextCommand");
                return status;
        }

        return status;
}

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

        // Deregister the tool command and the context creation command
        //
        status = plugin.deregisterContextCommand( "helixToolContext",
                                                                                          "helixToolCmd" );
        if (!status) {
                status.perror("deregisterContextCommand");
                return status;
        }

        return status;
}

Autodesk® Maya® 2011 © 1997-2010 Autodesk, Inc. All rights reserved. Generated with doxygen 1.5.6