//- // ========================================================================== // 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. // ========================================================================== //+ /* This example demonstrates how to use the PointOnSurface manipulator in the API. This example uses three classes to accomplish this task: First, a context command (surfaceBumpContext) is provided to create instances of the context. Next, a custom selection context (SurfaceBumpContext) is created to manage the surface manipulator. Finally, the surface manipulator is provided as a custom node class. Loading and unloading: ---------------------- The manipulator context and tool button can be created with the following mel commands: surfaceBumpContext; setParent Shelf1; toolButton -cl toolCluster -t surfaceBumpContext1 -i1 "moveToolManip.xpm" surfaceBumpManip; If the preceding commands were used to create the manipulator context, the following commands can destroy it: deleteUI surfaceBumpContext1; deleteUI surfaceBumpManip; If the plugin is loaded and unloaded frequently (eg. during testing), it is useful to make these command sequences into shelf buttons. How to use: ----------- Once the tool button has been created using the script above, select the tool button then click on a nurbs surface object. The PointOnSurface manipulator should appear on the object. Then drag the manipulator over the surface of the object and a bump should appear on the CV nearest to the manipulator. If the bump is too small, try adjusting the BUMP_SCALE value and recompile. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Manipulators #include // BUMP_SCALE is the factor indicating how far the bump created by the // manipulator should protrude from the object surface. The value is treated // as a distance in object space. Note that this value is how far the CV is // moved away from the surface, not the actual distance that the surface is // moved at the point. // static const double BUMP_SCALE = 0.5; // This is a utility method used to find the surface plug that should be used // to connect a pointOnSurface manipulator to a surface. The method takes the // input node (which must be a DAG node whose child is the nurbs shape) and // finds the nurbs shape node (shape) and the surface plug (plug). // MStatus findSurfacePlug(const MObject& node, MPlug& plug, MObject& shape) { if (!node.hasFn(MFn::kDagNode)) { MGlobal::displayError("Nodes passed to findSurfacePlug must be DAG" " nodes"); return MS::kFailure; } MFnDagNode nodeFn(node); if (nodeFn.childCount() != 1) { MGlobal::displayError("DAG node must have 1 child"); return MS::kFailure; } shape = nodeFn.child(0); if (!shape.hasFn(MFn::kNurbsSurface)) { MGlobal::displayError("Child node is not a nurbs surface"); shape = MObject::kNullObj; return MS::kFailure; } // Find the connection to the create attribute on the shape node. // MFnDependencyNode shapeNode(shape); plug = shapeNode.findPlug("create"); return MS::kSuccess; } ///////////////////////////////////////////////////////////// // // surfaceBumpManip // // This class implements the example pointOnSurface manipulator // ///////////////////////////////////////////////////////////// class surfaceBumpManip : public MPxManipContainer { public: surfaceBumpManip(); virtual ~surfaceBumpManip(); static void * creator(); static MStatus initialize(); virtual MStatus createChildren(); virtual MStatus connectToDependNode(const MObject &node); virtual void draw(M3dView &view, const MDagPath &path, M3dView::DisplayStyle style, M3dView::DisplayStatus status); // Callback function MManipData surfacePointChangedCallback(unsigned index); public: static MTypeId id; private: MDagPath fPointOnSurfaceManip; MObject fSurfaceShape; // Previous parameters int saved_u,saved_v; // Previous position of the CV MPoint savedPoint; unsigned dummyPlugIndex; }; MTypeId surfaceBumpManip::id( 0x80023 ); surfaceBumpManip::surfaceBumpManip() : saved_u(-1),saved_v(-1) { // The constructor must not call createChildren for user-defined // manipulators. } surfaceBumpManip::~surfaceBumpManip() { } void *surfaceBumpManip::creator() { return new surfaceBumpManip(); } MStatus surfaceBumpManip::initialize() { return MPxManipContainer::initialize(); } MStatus surfaceBumpManip::createChildren() { MStatus stat = MStatus::kSuccess; // Add the pointOnSurface manip // fPointOnSurfaceManip = addPointOnSurfaceManip("surfaceBumpManip", "point"); MFnPointOnSurfaceManip pointOnSurfaceManip(fPointOnSurfaceManip); pointOnSurfaceManip.setDrawSurface(true); pointOnSurfaceManip.setDrawArrows(true); pointOnSurfaceManip.setParameters(0.0,0.0); return stat; } MStatus surfaceBumpManip::connectToDependNode(const MObject &node) { MStatus stat; MFnPointOnSurfaceManip pointOnSurfaceManip(fPointOnSurfaceManip); // Connect to the nurbs surface plug. The surface plug indicates the // surface that should be used as the source for the uv manipulation. // MPlug surfacePlug; findSurfacePlug(node, surfacePlug, fSurfaceShape); if (!surfacePlug.isNull()) { MGlobal::displayInfo(MString("Using surface plug: ") + surfacePlug.name()); stat = pointOnSurfaceManip.connectToSurfacePlug(surfacePlug); if (stat != MStatus::kSuccess) { MGlobal::displayError("Could not connect surface plug"); return stat; } } else { MGlobal::displayError("Error finding surface plug"); return MS::kFailure; } // Add a dynamic attribute to the node to be computed by the manipToPlug // conversion function. For this manipulator, the manipToPlug conversion // function is used as a callback so this plug is just a placeholder. // // Note that this plugin leaves the dummy plugs on the nodes it manipulates. // MFnDependencyNode nodeFn(node); MPlug dummyPlug = nodeFn.findPlug("dummyPlug", &stat); if (dummyPlug.isNull()) { MFnNumericAttribute attributeFn; MObject attr = attributeFn.create("dummyPlug", "dp", MFnNumericData::k3Double); nodeFn.addAttribute(attr, MFnDependencyNode::kLocalDynamicAttr); dummyPlug = nodeFn.findPlug("dummyPlug", &stat); if (dummyPlug.isNull()) { MGlobal::displayError("Could not find dummyPlug on the manipulator."); return MS::kFailure; } } // Create a manipToPlug callback for the param plug. For now, the plug // that is computed is irrelevant (we only care about the callback being // invoked), so we pass in the dummy plug. // dummyPlugIndex = addManipToPlugConversionCallback(dummyPlug, (manipToPlugConversionCallback) &surfaceBumpManip::surfacePointChangedCallback); // Retrieve the transformation from the object and set it in the // manipulator. This allows the manipulator to display with the object // it is manipulating. // MFnTransform transform(node); MTransformationMatrix matrix = transform.transformation(); pointOnSurfaceManip.set(matrix); finishAddingManips(); MPxManipContainer::connectToDependNode(node); return stat; } void surfaceBumpManip::draw(M3dView & view, const MDagPath & path, M3dView::DisplayStyle style, M3dView::DisplayStatus status) { // Uses default manipulator drawing to draw the pointOnSurface manip // MPxManipContainer::draw(view, path, style, status); } MManipData surfaceBumpManip::surfacePointChangedCallback(unsigned index) { // Always return (0.0,0.0,0.0) to the dummy plug. // MFnNumericData numericData; MObject obj = numericData.create( MFnNumericData::k3Double ); numericData.setData(0.0,0.0,0.0); if (index != dummyPlugIndex) { MGlobal::displayError("Invalid index in surface point changed callback!"); return obj; } MFnNurbsSurface nurbsSurface(fSurfaceShape); // Get the parameter values from the node // double u = 0.0; double v = 0.0; MFnPointOnSurfaceManip pointOnSurfaceManip(fPointOnSurfaceManip); pointOnSurfaceManip.getParameters(u,v); // Uncomment to see the parameters that are received by the callback. /* MGlobal::displayInfo(MString("Num CVs are: ") + nurbsSurface.numSpansInU() + "," + nurbsSurface.numSpansInV()); MGlobal::displayInfo(MString("Parameters are: ") + u + "," + v); */ // // Snap the parameters to the nearest CV. This code assumes that CVs are // a unit distance apart in both the U and V directions. // int u_int = 0; int v_int = 0; if (nurbsSurface.formInU() == MFnNurbsSurface::kPeriodic) { u_int = (int)(floor(u+1.5)) % nurbsSurface.numSpansInU(); if (u_int < 0) u_int += nurbsSurface.numSpansInU(); } else { u_int = (int)(floor(u+1.5)); } if (nurbsSurface.formInV() == MFnNurbsSurface::kPeriodic) { v_int = (int)(floor(v+1.5)) % nurbsSurface.numSpansInV(); if (v_int < 0) v_int += nurbsSurface.numSpansInV(); } else { v_int = (int)(floor(v+1.5)); } // Uncomment to see the CVs that were determined from the parameters. /* MGlobal::displayInfo(MString("Int params are: ") + u_int + "," + v_int); */ // Is it the same CV? if (u_int == saved_u && v_int == saved_v) { return MManipData(obj); } // Is it the first one? We need a special case to initialize the class // variables. if (saved_u == -1) { nurbsSurface.getCV(u_int,v_int,savedPoint,MSpace::kObject); saved_u = u_int; saved_v = v_int; } // Set the old CV back nurbsSurface.setCV(saved_u,saved_v,savedPoint,MSpace::kObject); // Get the position for this CV and store the u and v values nurbsSurface.getCV(u_int,v_int,savedPoint,MSpace::kObject); saved_u = u_int; saved_v = v_int; // Move the CV out by the normal MPoint perturbedPosition = savedPoint + BUMP_SCALE*nurbsSurface.normal((double)u_int,(double)v_int,MSpace::kObject); nurbsSurface.setCV(u_int,v_int,perturbedPosition,MSpace::kObject); return MManipData(obj); } ///////////////////////////////////////////////////////////// // // SurfaceBumpContext // // This class is a simple context for supporting a pointOnSurface manip. // ///////////////////////////////////////////////////////////// class SurfaceBumpContext : public MPxSelectionContext { public: SurfaceBumpContext(); virtual void toolOnSetup(MEvent &event); virtual void toolOffCleanup(); // Callback issued when selection list changes static void updateManipulators(void * data); private: MCallbackId id1; }; SurfaceBumpContext::SurfaceBumpContext() { MString str("Plugin Surface Bump Manipulator"); setTitleString(str); } void SurfaceBumpContext::toolOnSetup(MEvent &) { MString str("Drag the manipulator around the surface"); setHelpString(str); updateManipulators(this); MStatus status; id1 = MModelMessage::addCallback(MModelMessage::kActiveListModified, updateManipulators, this, &status); if (!status) { MGlobal::displayError("Model addCallback failed"); } } void SurfaceBumpContext::toolOffCleanup() { MStatus status; status = MModelMessage::removeCallback(id1); if (!status) { MGlobal::displayError("Model remove callback failed"); } MPxContext::toolOffCleanup(); } void SurfaceBumpContext::updateManipulators(void * data) { MStatus stat = MStatus::kSuccess; SurfaceBumpContext * ctxPtr = (SurfaceBumpContext *) data; ctxPtr->deleteManipulators(); MSelectionList list; stat = MGlobal::getActiveSelectionList(list); MItSelectionList iter(list, MFn::kInvalid, &stat); if (MS::kSuccess == stat) { for (; !iter.isDone(); iter.next()) { // Make sure the selection list item is a depend node and has the // required plugs before manipulating it. // MObject dependNode; iter.getDependNode(dependNode); if (dependNode.isNull() || !dependNode.hasFn(MFn::kDependencyNode)) { MGlobal::displayWarning("Object in selection list is not " "a depend node."); continue; } // Add manipulator to the selected object // MString manipName ("surfaceBumpManip"); MObject manipObject; surfaceBumpManip* manipulator = (surfaceBumpManip *) surfaceBumpManip::newManipulator( manipName, manipObject); if (NULL != manipulator) { // Add the manipulator // ctxPtr->addManipulator(manipObject); // Connect the manipulator to the object in the selection list. // if (!manipulator->connectToDependNode(dependNode)) { MFnDependencyNode dependNodeFn(dependNode); MGlobal::displayWarning("Error connecting manipulator to" " object: " + dependNodeFn.name()); } } } } } ///////////////////////////////////////////////////////////// // // surfaceBumpContext // // This is the command that will be used to create instances // of our context. // ///////////////////////////////////////////////////////////// class surfaceBumpContext : public MPxContextCommand { public: surfaceBumpContext() {}; virtual MPxContext * makeObj(); public: static void* creator(); }; MPxContext *surfaceBumpContext::makeObj() { return new SurfaceBumpContext(); } void *surfaceBumpContext::creator() { return new surfaceBumpContext; } /////////////////////////////////////////////////////////////////////// // // The following routines are used to register/unregister // the context and manipulator // /////////////////////////////////////////////////////////////////////// MStatus initializePlugin(MObject obj) { MStatus status; MFnPlugin plugin(obj, "Alias", "6.0", "Any"); status = plugin.registerContextCommand("surfaceBumpContext", &surfaceBumpContext::creator); if (!status) { MGlobal::displayError("Error registering surfaceBumpContext command"); return status; } status = plugin.registerNode("surfaceBumpManip", surfaceBumpManip::id, &surfaceBumpManip::creator, &surfaceBumpManip::initialize, MPxNode::kManipContainer); if (!status) { MGlobal::displayError("Error registering surfaceBumpManip node"); return status; } return status; } MStatus uninitializePlugin(MObject obj) { MStatus status; MFnPlugin plugin(obj); status = plugin.deregisterContextCommand("surfaceBumpContext"); if (!status) { MGlobal::displayError("Error deregistering surfaceBumpContext command"); return status; } status = plugin.deregisterNode(surfaceBumpManip::id); if (!status) { MGlobal::displayError("Error deregistering surfaceBumpManip node"); return status; } return status; }