//- // ========================================================================== // Copyright (C) 1995 - 2005 Alias Systems Corp. and/or its licensors. All // rights reserved. // // The coded instructions, statements, computer programs, and/or related // material (collectively the "Data") in these files are provided by Alias // Systems Corp. ("Alias") and/or its licensors for the exclusive use of the // Customer (as defined in the Alias Software License Agreement that // accompanies this Alias software). Such Customer has the right to use, // modify, and incorporate the Data into other products and to distribute such // products for use by end-users. // // THE DATA IS PROVIDED "AS IS". ALIAS HEREBY DISCLAIMS ALL WARRANTIES // RELATING TO THE DATA, INCLUDING, WITHOUT LIMITATION, ANY AND ALL EXPRESS OR // IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. IN NO EVENT SHALL ALIAS BE LIABLE FOR ANY DAMAGES // WHATSOEVER, WHETHER DIRECT, INDIRECT, SPECIAL, OR PUNITIVE, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, OR IN EQUITY, // ARISING OUT OF ACCESS TO, USE OF, OR RELIANCE UPON THE DATA. // ========================================================================== //+ // // 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 #include #include #include #include #include #include "rockingTransform.h" #ifndef M_PI #include #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 = 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); }