rockingTransformCheck/rockingTransformCheck.cpp
 
 
 
rockingTransformCheck/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;
}