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

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

    //
    //  squareScaleManip.cpp
    //      Creates manipulator node squareScaleManipulator
    //      Creates command squareManipCmd
    //
    //  This example demonstrates how to use the MPxManipulatorNode
    //  class along with a command to create a user defined
    //  manipulator.  The manipulator created is a simple square
    //  with the 4 sides as OpenGL pickable components.  As you
    //  move the pickable component, selected transforms have
    //  their scale attribute modified.
    //  A corresponding command is used to create and delete
    //  the manipulator node and to support undo/redo etc.

    /*

    // To show this example using MEL, run the following:
    loadPlugin squareScaleManip.so;
    squareManipCmd -create;

    // To delete the manipulator using MEL:
    squareManipCmd -delete;

    */

    #include "squareScaleManip.h"

    #include <maya/MIOStream.h>
    #include <maya/MMatrix.h>
    #include <maya/MVector.h>
    #include <maya/MSelectionList.h>
    #include <maya/MItSelectionList.h>
    #include <maya/MFnTransform.h>
    #include <maya/MGlobal.h>
    #include <maya/MFnCamera.h>
    #include <maya/MTemplateCommand.h>
    #include <maya/MDrawRegistry.h>
    #include <maya/MDrawContext.h>
    #include <maya/MUserData.h>
    #include <maya/MStateManager.h>

    // Statics
    MTypeId squareScaleManipulator::id( 0x81046 );
    MString squareScaleManipulator::classification("drawdb/geometry/manip/squareScaleManipulator");
    MString squareScaleManipulator::registrantId("SquareScaleManipPlugin");
    const MPoint squareScaleManipulator::topLeft(-0.5f, 0.5f, 0.0f);
    const MPoint squareScaleManipulator::topRight(0.5f, 0.5f, 0.0f);
    const MPoint squareScaleManipulator::bottomLeft(-0.5f, -0.5f, 0.0f);
    const MPoint squareScaleManipulator::bottomRight(0.5f, -0.5f, 0.0f);

    //
    // class implementation
    //
    squareScaleManipulator::squareScaleManipulator()
    : MPxManipulatorNode()
    , activeName(0)
    , topName(0)
    , rightName(0)
    , bottomName(0)
    , leftName(0)
    , mousePointGlName(MPoint())
    {
        // Populate initial points
        topLeft.get(tl);
        topRight.get(tr);
        bottomLeft.get(bl);
        bottomRight.get(br);
        // Setup the plane with a point on the
        // plane along with a normal
        MPoint pointOnPlane(topLeft);
        // Normal = cross product of two vectors on the plane
        MVector normalToPlane =
            (MVector(topLeft) - MVector(topRight)) ^
            (MVector(topRight) - MVector(bottomRight));
        // Necessary to normalize
        normalToPlane.normalize();
        // Plane defined by a point and a normal
        plane.setPlane(pointOnPlane, normalToPlane);
    }

    squareScaleManipulator::~squareScaleManipulator()
    {
    }

    // virtual
    void squareScaleManipulator::draw(
        M3dView &view,
        const MDagPath &path,
        M3dView::DisplayStyle style,
        M3dView::DisplayStatus status)
    {
        // Are we in the right view
        MDagPath dpath;
        view.getCamera(dpath);
        if (!shouldDraw(dpath))
        {
            return;
        }

        // do the draw using common helper method
        view.beginGL();

        // Get the starting value of the pickable items
        MGLuint glPickableItem;
        glFirstHandle(glPickableItem);

        // Top
        topName = glPickableItem;
        // Place before you draw the manipulator component that can
        // be pickable.
        colorAndName(view, glPickableItem, true, mainColor());
        glBegin(GL_LINES);
            glVertex3fv(tl);
            glVertex3fv(tr);
        glEnd();

        // Right
        glPickableItem++;
        rightName = glPickableItem;
        colorAndName(view, glPickableItem, true, mainColor());
        glBegin(GL_LINES);
            glVertex3fv(tr);
            glVertex3fv(br);
        glEnd();

        // Bottom
        glPickableItem++;
        bottomName = glPickableItem;
        colorAndName(view, glPickableItem, true, mainColor());
        glBegin(GL_LINES);
            glVertex3fv(br);
            glVertex3fv(bl);
        glEnd();

        // Left
        glPickableItem++;
        leftName = glPickableItem;
        colorAndName(view, glPickableItem, true, mainColor());
        glBegin(GL_LINES);
            glVertex3fv(bl);
            glVertex3fv(tl);
        glEnd();

        view.endGL();
    }

    // virtual
    MStatus squareScaleManipulator::doPress(M3dView& view)
    {
        // Reset the mousePoint information on a new press
        mousePointGlName = MPoint::origin;
        updateDragInformation();
        return MS::kSuccess;
    }

    // virtual
    MStatus squareScaleManipulator::doDrag(M3dView& view)
    {
        updateDragInformation();
        return MS::kSuccess;
    }

    // virtual
    MStatus squareScaleManipulator::doRelease(M3dView& view)
    {
        // Scale nodes on the selection list. Implementation
        // is very simple and will not support undo.
        MSelectionList list;
        MGlobal::getActiveSelectionList(list);
        for (MItSelectionList iter(list); !iter.isDone(); iter.next())
        {
            MObject node;
            MStatus status;
            iter.getDependNode(node);
            MFnTransform xform(node, &status);
            if (MS::kSuccess == status)
            {
                double newScale[3];
                newScale[0] = mousePointGlName.x + 1;
                newScale[1] = mousePointGlName.y + 1;
                newScale[2] = mousePointGlName.z + 1;
                xform.setScale(newScale);
            }
        }
        return MS::kSuccess;
    }

    MStatus squareScaleManipulator::updateDragInformation()
    {
        // Find the mouse point in local space
        MPoint localMousePoint;
        MVector localMouseDirection;
        if (MS::kFailure == mouseRay(localMousePoint, localMouseDirection))
            return MS::kFailure;

        // Find the intersection of the mouse point with the
        // manip plane
        MPoint mouseIntersectionWithManipPlane;
        if (!plane.intersect(localMousePoint, localMouseDirection, mouseIntersectionWithManipPlane))
            return MS::kFailure;

        mousePointGlName = mouseIntersectionWithManipPlane;

        if (glActiveName(activeName))
        {
            // Reset draw points
            topLeft.get(tl);
            topRight.get(tr);
            bottomLeft.get(bl);
            bottomRight.get(br);
            float* start = 0;
            float* end = 0;
            if (activeName == topName)
            {
                start = tl;
                end = tr;
            }
            if (activeName == bottomName)
            {
                start = bl;
                end = br;
            }
            if (activeName == rightName)
            {
                start = tr;
                end = br;
            }
            if (activeName == leftName)
            {
                start = tl;
                end = bl;
            }
            if (start && end)
            {
                // Find a vector on the plane
                lineMath line;
                MPoint a(start[0], start[1], start[2]);
                MPoint b(end[0], end[1], end[2]);
                MPoint vab = a - b;
                // Define line with a point and a vector on the plane
                line.setLine(start, vab);
                MPoint cpt;
                // Find the closest point so that we can get the
                // delta change of the mouse in local space
                if (line.closestPoint(mousePointGlName, cpt))
                {
                    mousePointGlName.x -= cpt.x;
                    mousePointGlName.y -= cpt.y;
                    mousePointGlName.z -= cpt.z;
                }
                // Move draw points
                start[0] += (float)mousePointGlName.x;
                start[1] += (float)mousePointGlName.y;
                start[2] +=(float) mousePointGlName.z;
                end[0] += (float)mousePointGlName.x;
                end[1] += (float)mousePointGlName.y;
                end[2] +=(float) mousePointGlName.z;
            }
        }
        return MS::kSuccess;
    }

    bool squareScaleManipulator::shouldDraw(const MDagPath& cameraPath) const
    {
        MStatus status;
        MFnCamera camera(cameraPath, &status);
        if (!status)
        {
            return false;
        }

        const char* nameBuffer = camera.name().asChar();
        if (0 == nameBuffer)
        {
            return false;
        }

        if ((0 == strstr(nameBuffer,"persp")) &&
            (0 == strstr(nameBuffer,"front")))
        {
            return false;
        }

        return true;
    }

    //
    // Draw override implementation for VP2.0
    //
    class squareScaleManipulatorData : public MUserData
    {
    public:
        enum ManipEdge
        {
            kNone,
            kTop,
            kRight,
            kBottom,
            kLeft
        };
        squareScaleManipulatorData()
        : MUserData(false) // don't delete after draw
        , tl(0), tr(0), br(0), bl(0), activeEdge(kNone) {}
        virtual ~squareScaleManipulatorData() {}
        float* tl; float* tr; float* br; float* bl;
        ManipEdge activeEdge;
    };

    squareScaleManipulatorOverride::squareScaleManipulatorOverride(const MObject& obj)
    : MHWRender::MPxDrawOverride(obj, squareScaleManipulatorOverride::draw)
    {
    }

    squareScaleManipulatorOverride::~squareScaleManipulatorOverride()
    {
    }

    MHWRender::DrawAPI squareScaleManipulatorOverride::supportedDrawAPIs() const
    {
        return MHWRender::kOpenGL;
    }

    bool squareScaleManipulatorOverride::isBounded(
        const MDagPath& objPath,
        const MDagPath& cameraPath) const
    {
        return true;
    }

    MBoundingBox squareScaleManipulatorOverride::boundingBox(
        const MDagPath& objPath,
        const MDagPath& cameraPath) const
    {
        return MBoundingBox(
            squareScaleManipulator::topLeft,
            squareScaleManipulator::bottomRight);
    }

    bool squareScaleManipulatorOverride::disableInternalBoundingBoxDraw() const
    {
        return true;
    }

    MUserData* squareScaleManipulatorOverride::prepareForDraw(
        const MDagPath& objPath,
        const MDagPath& cameraPath,
        const MHWRender::MFrameContext& frameContext,
        MUserData* oldData)
    {
        // get the node
        MStatus status;
        MFnDependencyNode node(objPath.node(), &status);
        if (!status) return NULL;
        squareScaleManipulator* manipNode =
            dynamic_cast<squareScaleManipulator*>(node.userNode());
        if (!manipNode) return NULL;

        // access/create user data for draw callback
        squareScaleManipulatorData* data =
            dynamic_cast<squareScaleManipulatorData*>(oldData);
        if (!data)
        {
            data = new squareScaleManipulatorData();
        }

        // populate user data from the node
        data->tl = data->tr = data->br = data->bl = 0;
        data->activeEdge = squareScaleManipulatorData::kNone;
        if (manipNode->shouldDraw(cameraPath))
        {
            data->tl = manipNode->tl;
            data->tr = manipNode->tr;
            data->br = manipNode->br;
            data->bl = manipNode->bl;
            if (manipNode->activeName == manipNode->topName)
            {
                data->activeEdge = squareScaleManipulatorData::kTop;
            }
            else if (manipNode->activeName == manipNode->rightName)
            {
                data->activeEdge = squareScaleManipulatorData::kRight;
            }
            else if (manipNode->activeName == manipNode->bottomName)
            {
                data->activeEdge = squareScaleManipulatorData::kBottom;
            }
            else if (manipNode->activeName == manipNode->leftName)
            {
                data->activeEdge = squareScaleManipulatorData::kLeft;
            }
        }

        return data;
    }

    void squareScaleManipulatorOverride::draw(
        const MHWRender::MDrawContext& context,
        const MUserData* data)
    {
        MHWRender::MStateManager* stateMgr = context.getStateManager();
        const squareScaleManipulatorData* manipData =
            dynamic_cast<const squareScaleManipulatorData*>(data);
        if (stateMgr && manipData &&
            manipData->tl && manipData->tr && manipData->br && manipData->bl)
        {
            // turn off depth to get manip to draw on top of everything
            const MHWRender::MDepthStencilState* oldDepthState = stateMgr->getDepthStencilState();
            if (oldDepthState)
            {
                static const MHWRender::MDepthStencilState* depthState = NULL;
                if (!depthState)
                {
                    MHWRender::MDepthStencilStateDesc desc;
                    desc.depthEnable = false;
                    desc.depthWriteEnable = false;
                    depthState = stateMgr->acquireDepthStencilState(desc);
                }
                if (depthState)
                {
                    stateMgr->setDepthStencilState(depthState);
                }
            }

            // draw manip
            static const float dormantColor[4] = { 0.39f, 0.94f, 1.0f, 1.0f };
            static const float activeColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
            glColor4fv(manipData->activeEdge == squareScaleManipulatorData::kTop ? activeColor : dormantColor);
            glBegin(GL_LINES);
                glVertex3fv(manipData->tl);
                glVertex3fv(manipData->tr);
            glEnd();
            glColor4fv(manipData->activeEdge == squareScaleManipulatorData::kRight ? activeColor : dormantColor);
            glBegin(GL_LINES);
                glVertex3fv(manipData->tr);
                glVertex3fv(manipData->br);
            glEnd();
            glColor4fv(manipData->activeEdge == squareScaleManipulatorData::kBottom ? activeColor : dormantColor);
            glBegin(GL_LINES);
                glVertex3fv(manipData->br);
                glVertex3fv(manipData->bl);
            glEnd();
            glColor4fv(manipData->activeEdge == squareScaleManipulatorData::kLeft ? activeColor : dormantColor);
            glBegin(GL_LINES);
                glVertex3fv(manipData->bl);
                glVertex3fv(manipData->tl);
            glEnd();

            // restore old depth state
            if (oldDepthState)
            {
                stateMgr->setDepthStencilState(oldDepthState);
            }
        }
    }

    //
    // Template command that creates and deletes the manipulator
    //
    class squareManipCmd;
    char cmdName[] = "squareManipCmd";
    char nodeName[] = "squareScaleManipulator";

    class squareManipCmd : public MTemplateCreateNodeCommand<squareManipCmd,cmdName,nodeName>
    {
    public:
        //
        squareManipCmd()
        {}
    };

    static squareManipCmd _squareManipCmd;

    //
    // Static methods
    //
    void* squareScaleManipulator::creator()
    {
        return new squareScaleManipulator();
    }

    MStatus squareScaleManipulator::initialize()
    {
        // No-op
        return MS::kSuccess;
    }

    //
    //  Entry points
    //

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

        status = plugin.registerNode(
            nodeName,
            squareScaleManipulator::id,
            squareScaleManipulator::creator,
            squareScaleManipulator::initialize,
            MPxNode::kManipulatorNode,
            &squareScaleManipulator::classification);
        if (!status) {
            status.perror("registerNode");
            return status;
        }

        status = _squareManipCmd.registerCommand( obj );
        if (!status) {
            status.perror("registerCommand");
            return status;
        }

        status = MHWRender::MDrawRegistry::registerDrawOverrideCreator(
            squareScaleManipulator::classification,
            squareScaleManipulator::registrantId,
            squareScaleManipulatorOverride::Creator);
        if (!status) {
            status.perror("registerDrawOverrideCreator");
            return status;
        }

        return status;
    }

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

        status = plugin.deregisterNode( squareScaleManipulator::id );
        if (!status) {
            status.perror("deregisterNode");
            return status;
        }
        status = _squareManipCmd.deregisterCommand( obj );
        if (!status) {
            status.perror("deregisterCommand");
            return status;
        }

        status = MHWRender::MDrawRegistry::deregisterDrawOverrideCreator(
            squareScaleManipulator::classification,
            squareScaleManipulator::registrantId);
        if (!status) {
            status.perror("deregisterDrawOverrideCreator");
            return status;
        }

        return status;
    }