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

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

    //
    // Example custom transform:
    //  This plug-in implements an example custom transform that
    //  can be used to perform a rocking motion around the X axix.
    //  Geometry of any rotation can be made a child of this transform
    //  to demonstrate the effect.
    //  The plug-in contains two pieces:
    //  1. The custom transform node -- rockingTransformNode
    //  2. The custom transformation matrix -- rockingTransformMatrix
    //  These classes are used together in order to implement the
    //  rocking motion.  Note that the rock attribute is stored outside
    //  of the regular transform attributes.
    //
    // MEL usage:
    /*
        // Create a rocking transform and make a rotated plane
        // its child.
        loadPlugin rockingTransform;
        file -f -new;
        polyPlane -w 1 -h 1 -sx 10 -sy 10 -ax 0 1 0 -tx 1 -ch 1;
        select -r pPlane1 ;
        rotate -r -ws -15 -15 -15 ;
        createNode rockingTransform;
        parent pPlane1 rockingTransform1;
        setAttr rockingTransform1.rockx 55;
    */
    //

    #include <maya/MPxTransform.h>
    #include <maya/MPxTransformationMatrix.h>
    #include <maya/MGlobal.h>
    #include <maya/MFnNumericAttribute.h>
    #include <maya/MTransformationMatrix.h>
    #include <maya/MIOStream.h>

    #include "rockingTransform.h"

    #ifndef M_PI
    #include <math.h>
    #endif

    //
    // Initialize our static class variables
    //
    MObject rockingTransformNode::aRockInX;
    MTypeId rockingTransformNode::id(kRockingTransformNodeID);
    MTypeId rockingTransformMatrix::id(kRockingTransformMatrixID);

    //
    // Implementation of our custom transformation matrix
    //

    //
    //  Constructor for matrix
    //
    rockingTransformMatrix::rockingTransformMatrix()
    {
        rockXValue = 0.0;
    }

    //
    // Creator for matrix
    //
    void *rockingTransformMatrix::creator()
    {
        return new rockingTransformMatrix();
    }

    //
    //  Utility method for getting the rock
    //  motion in the X axis
    //
    double rockingTransformMatrix::getRockInX() const
    {
        return rockXValue;
    }

    //
    //  Utility method for setting the rcok
    //  motion in the X axis
    //
    void rockingTransformMatrix::setRockInX( double rock )
    {
        rockXValue = rock;
    }

    //
    // This method will be used to return information to
    // Maya.  Use the attributes which are outside of
    // the regular transform attributes to build a new
    // matrix.  This new matrix will be passed back to
    // Maya.
    //
    MMatrix rockingTransformMatrix::asMatrix() const
    {
        // Get the current transform matrix
        MMatrix m = ParentClass::asMatrix();
        // Initialize the new matrix we will calculate
        MTransformationMatrix tm( m );
        // Find the current rotation as a quaternion
        MQuaternion quat = rotation();
        // Convert the rocking value in degrees to radians
        DegreeRadianConverter conv;
        double newTheta = conv.degreesToRadians( getRockInX() );
        quat.setToXAxis( newTheta );
        // Apply the rocking rotation to the existing rotation
        tm.addRotationQuaternion( quat.x, quat.y, quat.z, quat.w, MSpace::kTransform );
        // Let Maya know what the matrix should be
        return tm.asMatrix();
    }

    MMatrix rockingTransformMatrix::asMatrix(double percent) const
    {
        MPxTransformationMatrix m(*this);

        //  Apply the percentage to the matrix components
        MVector trans = m.translation();
        trans *= percent;
        m.translateTo( trans );
        MPoint rotatePivotTrans = m.rotatePivot();
        rotatePivotTrans = rotatePivotTrans * percent;
        m.setRotatePivot( rotatePivotTrans );
        MPoint scalePivotTrans = m.scalePivotTranslation();
        scalePivotTrans = scalePivotTrans * percent;
        m.setScalePivotTranslation( scalePivotTrans );

        //  Apply the percentage to the rotate value.  Same
        // as above + the percentage gets applied
        MQuaternion quat = rotation();
        DegreeRadianConverter conv;
        double newTheta = conv.degreesToRadians( getRockInX() );
        quat.setToXAxis( newTheta );
        m.rotateBy( quat );
        MEulerRotation eulRotate = m.eulerRotation();
        m.rotateTo(  eulRotate * percent, MSpace::kTransform);

        //  Apply the percentage to the scale
        MVector s(scale(MSpace::kTransform));
        s.x = 1.0 + (s.x - 1.0)*percent;
        s.y = 1.0 + (s.y - 1.0)*percent;
        s.z = 1.0 + (s.z - 1.0)*percent;
        m.scaleTo(s, MSpace::kTransform);

        return m.asMatrix();
    }

    MMatrix rockingTransformMatrix::asRotateMatrix() const
    {
        // To be implemented
        return ParentClass::asRotateMatrix();
    }

    //
    // Implementation of our custom transform
    //

    //
    //  Constructor of the transform node
    //
    rockingTransformNode::rockingTransformNode()
    : ParentClass()
    {
        rockXValue = 0.0;
    }

    //
    //  Constructor of the transform node
    //
    rockingTransformNode::rockingTransformNode(MPxTransformationMatrix *tm)
    : ParentClass(tm)
    {
        rockXValue = 0.0;
    }

    //
    //  Post constructor method.  Have access to *this.  Node setup
    //  operations that do not go into the initialize() method should go
    //  here.
    //
    void rockingTransformNode::postConstructor()
    {
        //  Make sure the parent takes care of anything it needs.
        //
        ParentClass::postConstructor();

        //  The baseTransformationMatrix pointer should be setup properly
        //  at this point, but just in case, set the value if it is missing.
        //
        if (NULL == baseTransformationMatrix) {
            MGlobal::displayWarning("NULL baseTransformationMatrix found!");
            baseTransformationMatrix = new MPxTransformationMatrix();
        }

        MPlug aRockInXPlug(thisMObject(), aRockInX);
    }

    //
    //  Destructor of the rocking transform
    //
    rockingTransformNode::~rockingTransformNode()
    {
    }

    //
    //  Method that returns the new transformation matrix
    //
    MPxTransformationMatrix *rockingTransformNode::createTransformationMatrix()
    {
        return new rockingTransformMatrix();
    }

    //
    //  Method that returns a new transform node
    //
    void *rockingTransformNode::creator()
    {
        return new rockingTransformNode();
    }

    //
    //  Node initialize method.  We configure node
    //  attributes here.  Static method so
    //  *this is not available.
    //
    MStatus rockingTransformNode::initialize()
    {
        MFnNumericAttribute numFn;
        aRockInX = numFn.create("RockInX", "rockx", MFnNumericData::kDouble, 0.0);
        numFn.setKeyable(true);
        numFn.setAffectsWorldSpace(true);
        addAttribute(aRockInX);

        //  This is required so that the validateAndSet method is called
        mustCallValidateAndSet(aRockInX);
        return MS::kSuccess;
    }

    //
    //  Debugging method
    //
    const char* rockingTransformNode::className()
    {
        return "rockingTransformNode";
    }

    //
    //  Reset transformation
    //
    void  rockingTransformNode::resetTransformation (const MMatrix &matrix)
    {
        ParentClass::resetTransformation( matrix );
    }

    //
    //  Reset transformation
    //
    void  rockingTransformNode::resetTransformation (MPxTransformationMatrix *resetMatrix )
    {
        ParentClass::resetTransformation( resetMatrix );
    }

    //
    // A very simple implementation of validAndSetValue().  No lock
    // or limit checking on the rocking attribute is done in this method.
    // If you wish to apply locks and limits to the rocking attribute, you
    // would follow the approach taken in the rockingTransformCheck example.
    // Meaning you would implement methods similar to:
    //  * applyRotationLocks();
    //  * applyRotationLimits();
    //  * checkAndSetRotation();
    // but for the rocking attribute.  The method checkAndSetRotation()
    // would be called below rather than updating the rocking attribute
    // directly.
    //
    MStatus rockingTransformNode::validateAndSetValue(const MPlug& plug,
                                                    const MDataHandle& handle,
                                                    const MDGContext& context)
    {
        MStatus status = MS::kSuccess;

        //  Make sure that there is something interesting to process.
        //
        if (plug.isNull())
            return MS::kFailure;

        MDataBlock block = forceCache(*(MDGContext *)&context);
        MDataHandle blockHandle = block.outputValue(plug, &status);
        ReturnOnError(status);

        if ( plug == aRockInX )
        {
            // Update our new rock in x value
            double rockInX = handle.asDouble();
            blockHandle.set(rockInX);
            rockXValue = rockInX;

            // Update the custom transformation matrix to the
            // right rock value.
            rockingTransformMatrix *ltm = getRockingTransformMatrix();
            if (ltm)
                ltm->setRockInX(rockXValue);
            else
                MGlobal::displayError("Failed to get rock transform matrix");

            blockHandle.setClean();

            // Mark the matrix as dirty so that DG information
            // will update.
            dirtyMatrix();
        }

        // Allow processing for other attributes
        return ParentClass::validateAndSetValue(plug, handle, context);
    }

    //
    //  Method for returning the current rocking transformation matrix
    //
    rockingTransformMatrix *rockingTransformNode::getRockingTransformMatrix()
    {
        rockingTransformMatrix *ltm = (rockingTransformMatrix *) baseTransformationMatrix;
        return ltm;
    }

    //
    // Utility class
    //
    double DegreeRadianConverter::degreesToRadians( double degrees )
    {
         return degrees * ( M_PI/ 180.0 );
    }
    double DegreeRadianConverter::radiansToDegrees( double radians )
    {
        return radians * (180.0/M_PI);
    }