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

# rockingTransformCheck.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 -- rockingTransformCheckNode
    //  2. The custom transformation matrix -- rockingTransformCheckMatrix
    //  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 rockingTransformCheck;
        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 rockingTransformCheck;
        parent pPlane1 rockingTransformCheck1;
        setAttr rockingTransformCheck1.rockx 10;
    */
    //

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

    #include "rockingTransformCheck.h"

    //
    // Initialize our static class variables
    //
    MTypeId rockingTransformCheckNode::idCheck(kRockingTransformCheckNodeID);
    MTypeId rockingTransformCheckMatrix::idCheck(kRockingTransformCheckMatrixID);

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

    //
    //  Constructor for matrix
    //
    rockingTransformCheckMatrix::rockingTransformCheckMatrix()
    {
    }

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

    //
    // Implementation of our custom transform
    //

    //
    //  Constructor of the transform node
    //
    rockingTransformCheckNode::rockingTransformCheckNode()
    :   ParentClass()
    {
    }

    //
    //  Constructor of the transform node
    //
    rockingTransformCheckNode::rockingTransformCheckNode(MPxTransformationMatrix *tm)
    :   ParentClass(tm)
    {
    }

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

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

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

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

    //
    //  This method allows the custom transform to apply its own locking
    //  mechanism to rotation. Standard dependency graph attribute locking
    //  happens automatically and cannot be modified by custom nodes.
    //  If the plug should not be changed, then the value from the passed savedR
    //  argument should be return to be used in the transformation matrix.
    //
    MEulerRotation rockingTransformCheckNode::applyRotationLocks(const MEulerRotation &toTest,
                                        const MEulerRotation &savedRotation,
                                        MStatus *ReturnStatus )
    {
    #ifdef ALLOW_DG_TO_HANDLE_LOCKS
        // Allow the DG to handle locking.
        return toTest;
    #else
        //
        // Implement a simple lock by checking for an existing attribute
        // Use the following MEL to add the attribute:
        //  addAttr -ln "rotateLockPlug"
        //
        MStatus status;
        MObject object = thisMObject();
        MFnDependencyNode depNode( object );
        MObject rotateLockPlug = depNode.findPlug( "rotateLockPlug", &status );

        // If the lock does not exist that we return the updated value that has
        // been passed in
        if ( rotateLockPlug.isNull() )
            return toTest;

        // We have a lock.  Returned the original saved value of the
        // rotation.
        return savedRotation;
    #endif
    }

    MEulerRotation rockingTransformCheckNode::applyRotationLimits(const MEulerRotation &unlimitedRotation,
                                          MDataBlock & /*block*/,
                                          MStatus *ReturnStatus )
    {
    #ifdef CHECK_ROTATION_LIMITS_USING_ATTRIBUTES
        //
        // A more complete plug-in would take this approach
        //
        MEulerRotation newRotation = unlimitedRotation;
        MDGContext context = block.context();

        updateMatrixAttrs(minRotLimitEnable, context);
        updateMatrixAttrs(maxRotLimitEnable, context);

        double3 &minLimit = block.inputValue(minRotLimit).asDouble3();
        double3 &maxLimit = block.inputValue(maxRotLimit).asDouble3();

        unsigned ii = 0, jj = 0;
        for (jj = MFnTransform::kRotateMinX, ii = 0; ii < 3; ++ii, ++jj) {
            if (isLimited((MFnTransform::LimitType)jj) &&
                newRotation[ii] < minLimit[ii]) {
                newRotation[ii] = minLimit[ii];
            }

            if (isLimited((MFnTransform::LimitType)(++jj)) &&
                newRotation[ii] > maxLimit[ii]) {
                newRotation[ii] = maxLimit[ii];
            }
        }

        if ( ReturnStatus )
            *ReturnStatus = MS::kSuccess;

        return newRotation;
    #else
        //
        // For demonstration purposes we limit the rotation to 60
        // degrees and bypass the rotation limit attributes
        //
        DegreeRadianConverter conv;
        double degrees = conv.radiansToDegrees( unlimitedRotation.x );
        if ( degrees < 60 )
            return unlimitedRotation;
        MEulerRotation euler;
        euler.x = conv.degreesToRadians( 60.0 );
        if ( ReturnStatus )
            *ReturnStatus = MS::kSuccess;
        return euler;
    #endif
    }

    //
    //  Calls applyRotationLocks && applyRotationLimits
    //  This method verifies that the passed value can be set on the
    //  rotate plugs. In the base class, limits as well as locking are
    //  checked by this method.
    //
    //  The compute, validateAndSetValue, and rotateTo functions
    //  all use this method.
    //
    MStatus rockingTransformCheckNode::checkAndSetRotation(MDataBlock &block,
                                        const MPlug& plug,
                                        const MEulerRotation& newRotation,
                                        MSpace::Space space )
    {
        const MDGContext context = block.context();
        updateMatrixAttrs(context);

        MStatus status = MS::kSuccess;
        MEulerRotation outRotation = newRotation;
        if (context.isNormal()) {
            //  For easy reading.
            //
            MPxTransformationMatrix *xformMat = baseTransformationMatrix;

            //  Get the current translation in transform space for
            //  clamping and locking.
            //
            MEulerRotation savedRotation =
                xformMat->eulerRotation(MSpace::kTransform, &status);
            ReturnOnError(status);

            //  Translate to transform space, since the limit test needs the
            //  values in transform space. The locking test needs the values
            //  in the same space as the savedR value - which is transform
            //  space as well.
            //
            status = baseTransformationMatrix->rotateTo(newRotation, space);
            ReturnOnError(status);

            outRotation = xformMat->eulerRotation(MSpace::kTransform, &status);
            ReturnOnError(status);

            //  Now that everything is in the same space, apply limits
            //  and change the value to adhere to plug locking.
            //
            outRotation = applyRotationLimits(outRotation, block, &status);
            ReturnOnError(status);

            outRotation = applyRotationLocks(outRotation, savedRotation, &status);
            ReturnOnError(status);

            //  The value that remain is in transform space.
            //
            status = xformMat->rotateTo(outRotation, MSpace::kTransform);
            ReturnOnError(status);

            //  Get the value that was just set. It needs to be in transform
            //  space since it is used to set the datablock values at the
            //  end of this method. Getting the vaolue right before setting
            //  ensures that the transformation matrix and data block will
            //  be synchronized.
            //
            outRotation = xformMat->eulerRotation(MSpace::kTransform, &status);
            ReturnOnError(status);
        } else {
            //  Get the rotation for clamping and locking. This will get the
            //  rotate value in transform space.
            //
            double3 &s3 = block.inputValue(rotate).asDouble3();
            MEulerRotation savedRotation(s3[0], s3[1], s3[2]);

            //  Create a local transformation matrix for non-normal context
            //  calculations.
            //
            MPxTransformationMatrix *local = createTransformationMatrix();
            if (NULL == local) {
                MGlobal::displayError("rockingTransformCheck::checkAndSetRotation internal error");
                return status;
            }

            //  Fill the newly created transformation matrix.
            //
            status = computeLocalTransformation(local, block);
            if ( MS::kSuccess != status)
            {
                delete local;
                return status;
            }

            //  Translate the values to transform space. This will allow the
            //  limit and locking tests to work properly.
            //
            status = local->rotateTo(newRotation, space);
            if ( MS::kSuccess != status)
            {
                delete local;
                return status;
            }

            outRotation = local->eulerRotation(MSpace::kTransform, &status);
            if ( MS::kSuccess != status)
            {
                delete local;
                return status;
            }

            //  Apply limits
            //
            outRotation = applyRotationLimits(outRotation, block, &status);
            if ( MS::kSuccess != status)
            {
                delete local;
                return status;
            }

            outRotation = applyRotationLocks(outRotation, savedRotation, &status);
            if ( MS::kSuccess != status)
            {
                delete local;
                return status;
            }

            status = local->rotateTo(outRotation, MSpace::kTransform);
            if ( MS::kSuccess != status)
            {
                delete local;
                return status;
            }

            //  Get the rotate value in transform space for placement in the
            //  datablock.
            //
            outRotation = local->eulerRotation(MSpace::kTransform, &status);
            if ( MS::kSuccess != status)
            {
                delete local;
                return status;
            }

            delete local;
        }

        MDataHandle handle = block.outputValue(plug, &status);
        if ( MS::kSuccess != status)
        {
            return status;
        }

        if (plug == rotate) {
            handle.set(outRotation.x, outRotation.y, outRotation.z);
        } else if (plug == rotateX) {
            handle.set(outRotation.x);
        } else if (plug == rotateY) {
            handle.set(outRotation.y);
        } else {
            handle.set(outRotation.z);
        }

        return status;
    }

    //
    //  Method for returning the current rocking transformation matrix
    //
    rockingTransformCheckMatrix *rockingTransformCheckNode::getRockingTransformCheckMatrix()
    {
        rockingTransformCheckMatrix *ltm = (rockingTransformCheckMatrix *) baseTransformationMatrix;
        return ltm;
    }