//- // ========================================================================== // Copyright (C) 1995 - 2005 Alias Systems Corp. and/or its licensors. All // rights reserved. // // The coded instructions, statements, computer programs, and/or related // material (collectively the "Data") in these files are provided by Alias // Systems Corp. ("Alias") and/or its licensors for the exclusive use of the // Customer (as defined in the Alias Software License Agreement that // accompanies this Alias software). Such Customer has the right to use, // modify, and incorporate the Data into other products and to distribute such // products for use by end-users. // // THE DATA IS PROVIDED "AS IS". ALIAS HEREBY DISCLAIMS ALL WARRANTIES // RELATING TO THE DATA, INCLUDING, WITHOUT LIMITATION, ANY AND ALL EXPRESS OR // IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. IN NO EVENT SHALL ALIAS BE LIABLE FOR ANY DAMAGES // WHATSOEVER, WHETHER DIRECT, INDIRECT, SPECIAL, OR PUNITIVE, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, OR IN EQUITY, // ARISING OUT OF ACCESS TO, USE OF, OR RELIANCE UPON THE DATA. // ========================================================================== //+ //////////////////////////////////////////////////////////////////////// // // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(OSMac_MachO_) #include #include #else #include #include #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; MObject curve = 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_) ::aglSwapBuffers( view.display()); #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, "Alias", "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; }