buildRotationNode/buildRotationNode.cpp
 
 
 
buildRotationNode/buildRotationNode.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.
// ==========================================================================
//+

#include <string.h>
#include <maya/MIOStream.h>
#include <math.h>

#include <maya/MPxNode.h> 

#include <maya/MFnNumericAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnPlugin.h>

#include <maya/MString.h> 
#include <maya/MTypeId.h> 
#include <maya/MPlug.h>
#include <maya/MVector.h>
#include <maya/MMatrix.h>
#include <maya/MTransformationMatrix.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>

#define EPSILON                         0.001
#define ROTATE_ORDER_XYZ        0
#define ROTATE_ORDER_YZX        1
#define ROTATE_ORDER_ZXY        2
#define ROTATE_ORDER_XZY        3
#define ROTATE_ORDER_YXZ        4
#define ROTATE_ORDER_ZYX        5

#define McheckErr(stat,msg)         \
    if ( MS::kSuccess != stat ) {   \
                cerr << msg;                \
                return MS::kFailure;        \
        }
 
class buildRotation : public MPxNode
{
public:
                                                buildRotation();
        virtual                         ~buildRotation(); 

        virtual MStatus         compute( const MPlug& plug, MDataBlock& data );

        static  void*           creator();
        static  MStatus         initialize();

public:
        
        // Inputs
        static  MObject     up;           // Up vector
        static  MObject         upX;
        static  MObject         upY;
        static  MObject         upZ;
    static  MObject     forward;      // Front Vector
    static  MObject         forwardX;
    static  MObject         forwardY;
    static  MObject         forwardZ;
        static  MObject     rotateOrder;  // Desired rotation order

        // Outputs
        static  MObject     rotate;       // Rotation as a 3 Double
        static  MObject         rotateX;
        static  MObject         rotateY;
        static  MObject         rotateZ;
        static  MObject     rotateMatrix; // Rotation as a matrix

        static  MTypeId         id;           
};

MTypeId     buildRotation::id( 0x80004 );
MObject     buildRotation::up; 
MObject     buildRotation::upX;  
MObject     buildRotation::upY;    
MObject     buildRotation::upZ;     
MObject     buildRotation::forward;  
MObject     buildRotation::forwardX;  
MObject     buildRotation::forwardY;  
MObject     buildRotation::forwardZ;     
MObject     buildRotation::rotateOrder;    
MObject     buildRotation::rotate;    
MObject     buildRotation::rotateX;    
MObject     buildRotation::rotateY;   
MObject     buildRotation::rotateZ;  
MObject     buildRotation::rotateMatrix;   

buildRotation::buildRotation() {}
buildRotation::~buildRotation() {}

MStatus buildRotation::compute( const MPlug& plug, MDataBlock& data )
{
        
        MStatus returnStatus;
 
        if ((plug == rotate) || (plug.parent() == rotate) || (plug == rotateMatrix)) {
                MDataHandle upData = data.inputValue( up, &returnStatus );
                McheckErr(returnStatus,"ERROR getting up vector data");

                MDataHandle forwardData = data.inputValue( forward, &returnStatus );
                McheckErr(returnStatus,"ERROR getting forward vector data");

                MVector up = upData.asVector();
                MVector forward = forwardData.asVector();

                // Make sure that the up and forward vectors are orthogonal
                //
                if ( fabs( up * forward ) > EPSILON ) {
                        // Non-zero dot product
                        //
                        MVector orthoVec = up ^ forward;
                        MVector newForward = orthoVec ^ up;
                        if ( forward * newForward < 0.0 ) {
                                // Reverse the vector
                                //
                                newForward *= -1.0;
                        }
                        forward = newForward;
                }

                // Calculate the rotation required to align the y-axis with the up
                // vector
                //
                MTransformationMatrix firstRot;
                MVector rotAxis = MVector::yAxis ^ up;
                rotAxis.normalize();
                firstRot.setToRotationAxis( rotAxis, MVector::yAxis.angle( up ) );
                
                // Calculate the second rotation required to align the forward vector
                //
                MTransformationMatrix secondRot;
                MVector transformedForward = firstRot.asMatrix() * forward;
                transformedForward.normalize();
                double angle = transformedForward.angle( MVector::zAxis );
                if ( transformedForward.x < 0.0 ) {
                        // Compensate for the fact that the angle method returns
                        // the absolute value
                        //
                        angle *= -1.0;
                }
                secondRot.setToRotationAxis( up, angle );

                // Get the requested rotation order
                //
                MDataHandle orderHandle = data.inputValue( rotateOrder );
                short order = orderHandle.asShort();
                MTransformationMatrix::RotationOrder rotOrder;
                switch ( order ) {
                        case ROTATE_ORDER_XYZ:
                                rotOrder = MTransformationMatrix::kXYZ; break;
                        case ROTATE_ORDER_YZX:
                                rotOrder = MTransformationMatrix::kYZX; break;
                        case ROTATE_ORDER_ZXY:
                                rotOrder = MTransformationMatrix::kZXY; break;
                        case ROTATE_ORDER_XZY:
                                rotOrder = MTransformationMatrix::kXZY; break;
                        case ROTATE_ORDER_YXZ:
                                rotOrder = MTransformationMatrix::kYXZ; break;
                        case ROTATE_ORDER_ZYX:
                                rotOrder = MTransformationMatrix::kZYX; break;
                        default:
                                rotOrder = MTransformationMatrix::kInvalid; break;
                }

                MTransformationMatrix result = firstRot.asMatrix() * secondRot.asMatrix();
                result.reorderRotation( rotOrder );

                double rotation[3];
                result.getRotation( rotation, rotOrder, MSpace::kTransform );
                
                MDataHandle outputRot = data.outputValue( rotate );
                outputRot.set( rotation[0], rotation[1], rotation[2] );
                outputRot.setClean();

                MDataHandle outputMatrix = data.outputValue( rotateMatrix );
                outputMatrix.set( result.asMatrix() );
                outputMatrix.setClean();
        } else
                return MS::kUnknownParameter;

        return MS::kSuccess;
}

void* buildRotation::creator()
{
        return new buildRotation();
}

MStatus buildRotation::initialize()
{
        MFnNumericAttribute nAttr;
        MFnMatrixAttribute  mAttr;
        MFnUnitAttribute    uAttr;
        MFnEnumAttribute    eAttr;
        MStatus                         stat;

        // Set up inputs
        //
    upX = nAttr.create( "upX", "ux", MFnNumericData::kDouble );
            nAttr.setStorable(false);
    upY = nAttr.create( "upY", "uy", MFnNumericData::kDouble );
            nAttr.setStorable(false);
    upZ = nAttr.create( "upZ", "uz", MFnNumericData::kDouble );
            nAttr.setStorable(false);
        up = nAttr.create( "up", "u", upX, upY, upZ );
            nAttr.setDefault( 0.0, 1.0, 0.0 );
            nAttr.setStorable(false);

    forwardX = nAttr.create( "forwardX", "fx", MFnNumericData::kDouble, 0.0 );
            nAttr.setStorable(false);
    forwardY = nAttr.create( "forwardY", "fy", MFnNumericData::kDouble, 0.0 );
            nAttr.setStorable(false);
    forwardZ = nAttr.create( "forwardZ", "fz", MFnNumericData::kDouble, 1.0 );
            nAttr.setStorable(false);
        forward = nAttr.create( "forward", "f", forwardX, forwardY, forwardZ );
            nAttr.setDefault( 0.0, 0.0, 1.0 );
            nAttr.setStorable(false);

        rotateOrder = eAttr.create( "rotateOrder", "ro", ROTATE_ORDER_XYZ);
                eAttr.addField("xyz", ROTATE_ORDER_XYZ);
                eAttr.addField("yzx", ROTATE_ORDER_YZX);
                eAttr.addField("zxy", ROTATE_ORDER_ZXY);
                eAttr.addField("xzy", ROTATE_ORDER_XZY);
                eAttr.addField("yxz", ROTATE_ORDER_YXZ);
                eAttr.addField("zyx", ROTATE_ORDER_ZYX);
            eAttr.setStorable(false);

        // Set up outputs
        // 
    rotateX = uAttr.create( "rotateX", "rx", MFnUnitAttribute::kAngle, 0.0 );
            nAttr.setStorable(false);
    rotateY = uAttr.create( "rotateY", "ry", MFnUnitAttribute::kAngle, 0.0 );
            nAttr.setStorable(false);
    rotateZ = uAttr.create( "rotateZ", "rz", MFnUnitAttribute::kAngle, 0.0 );
            nAttr.setStorable(false);
        rotate = nAttr.create( "rotate", "r", rotateX, rotateY, rotateZ );
            nAttr.setStorable(false);
                
        rotateMatrix = mAttr.create( "rotateMatrix", "rm" );
            nAttr.setStorable(false);
                nAttr.setConnectable(true);

        stat = addAttribute( up );
                if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( forward );
                if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( rotate );
                if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( rotateOrder );
                if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( rotateMatrix );
                if (!stat) { stat.perror("addAttribute"); return stat;}

        stat = attributeAffects( up, rotate );
                if (!stat) { stat.perror("attributeAffects"); return stat;}
        stat = attributeAffects( up, rotateMatrix );
                if (!stat) { stat.perror("attributeAffects"); return stat;}
        stat = attributeAffects( forward, rotate );
                if (!stat) { stat.perror("attributeAffects"); return stat;}
        stat = attributeAffects( forward, rotateMatrix );
                if (!stat) { stat.perror("attributeAffects"); return stat;}
        stat = attributeAffects( rotateOrder, rotate );
                if (!stat) { stat.perror("attributeAffects"); return stat;}
        stat = attributeAffects( rotateOrder, rotateMatrix );
                if (!stat) { stat.perror("attributeAffects"); return stat;}

        return MS::kSuccess;
}

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

        status = plugin.registerNode( "buildRotation", buildRotation::id, 
                                                 buildRotation::creator, buildRotation::initialize );
        if (!status) {
                status.perror("registerNode");
                return status;
        }

        return status;
}

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

        status = plugin.deregisterNode( buildRotation::id );
        if (!status) {
                status.perror("deregisterNode");
                return status;
        }

        return status;
}