API ガイド > カスタム トランスフォーム ノードを作成する >

# カスタム トランスフォームのサンプル

次に、カスタム トランスフォームの使用例を示します。

カスタム トランスフォーム アトリビュートのサンプル

ここでは、Maya API 開発キットで使用できる rockingTransform のサンプルについて説明します。rockingTransform のサンプルは、X 軸の周りのロッキング モーションを含むアトリビュートを導入します。このロッキング モーションまたは回転は、通常の回転(Rotate)アトリビュートとは別に格納されますが、変換行列に組み込まれます。また、新しい rockXValue クラス変数にアクセスできるように、get/set メソッドがクラスに追加されています。

プロキシ変換行列の実装

rockingTransformMatrix クラスは MPxTransformationMatrix を継承して、asMatrix() などの仮想メソッドを定義します。

    class rockingTransformMatrix : public MPxTransformationMatrix
    {
        // A really simple implementation of MPxTransformationMatrix.
        // The methods include:
        // - Two accessor methods for getting and setting the
        // rock
        // - The virtual asMatrix() method which passes the matrix
        // back to Maya when the command "xform -q -ws -m" is invoked
        public:
            rockingTransformMatrix();
            static void *creator();

            virtual MMatrix asMatrix() const;
            virtual MMatrix asMatrix(double percent) const;
            virtual MMatrix asRotateMatrix() const;

            // Degrees
            double	getRockInX() const;
            void	setRockInX( double rock );

            static	MTypeId	id;
        protected:
            typedef MPxTransformationMatrix ParentClass;
            // Degrees
            double rockXValue;
    };

rockingTransformNode を実装する際には、MPxTransform から派生させて、createTransformationMatrix() や validateAndSet() などの必要な仮想メソッドをサポートします。また、このクラスには新しい aRockInX アトリビュートが追加されています。

    class rockingTransformNode : public MPxTransform
    {
        // A really simple custom transform.
        public:
            rockingTransformNode();
            rockingTransformNode(MPxTransformationMatrix *);
            virtual ~rockingTransformNode();
            virtual MPxTransformationMatrix *createTransformationMatrix();

            virtual void postConstructor();
            virtual MStatus validateAndSetValue(
                const MPlug& plug,
                const MDataHandle& handle,
                const MDGContext& context);

            virtual void resetTransformation (MPxTransformationMatrix *);
            virtual void resetTransformation (const MMatrix &);

            // Utility for getting the related rock matrix pointer
            rockingTransformMatrix *getRockingTransformMatrix();

            const char* 		className();
            static	void * 		creator();
            static MStatus	initialize();

            static	MTypeId	id;
        protected:
            // Degrees
            static	MObject aRockInX;
            double rockXValue;
            typedef MPxTransform ParentClass;
    };

プラグインの初期化

プラグインを初期化する場合、MFnPlugin を使用して新しいトランスフォーム ノードと関連する変換行列を登録する必要があります。トランスフォームを登録する場合、MTypeIds に格納する一意の識別子が必要です。

    MStatus initializePlugin( MObject obj )
    {
        MStatus status;
        MFnPlugin plugin(obj, "Autodesk", "6.5", "Any");
        status = plugin.registerTransform( "rockingTransform",
            rockingTransformNode::id, &rockingTransformNode::creator,
            &rockingTransformNode::initialize, &rockingTransformMatrix::creator,
            rockingTransformMatrix::id);
        if (!status) {
            status.perror("registerNode");
            return status;
        }
        return status;
    }

カスタム ノードの除去は、MFnPlugin の deregisterNode() メソッドをコールして uninitializePlugin() で実行されます。

    MStatus uninitializePlugin( MObject obj)
    {
        MStatus status;
        MFnPlugin plugin(obj);
        status = plugin.deregisterNode( rockingTransformNode::id );
        if (!status) {
            status.perror("deregisterNode");
            return status;
        }
        return status;
    }

rockingTransform 行列クラスの実装

次のサンプルは、constructor、creator、get/set メソッドの単純な実施例です。

    //
    // Matrix constructor. Initialize any
    // class variables.
    //
    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;
    }

asMatrix() メソッドは、カスタム変換行列を使用する上で非常に重要です。このメソッドは、カスタム トランスフォームから変換行列を要求する際にコールされます。次のサンプルの asMatrix() は、ParentClass::asMatrix() をコールして Maya の標準的な変換行列を計算します。その後、ロッキング モーションを使用して、計算された変換行列にクォータニオン回転が追加されます。この方法を使うと、カスタム トランスフォームは新しいアトリビュートを変換行列の出力に統合できます。

    //
    // 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();
    }

複数の asMatrix() メソッドが存在することに注意してください。カスタム トランスフォームが行列に与える影響によっては、すべての asMatrix() メソッドを実装する必要があります。

rockingTransformNode の実装

API を使用する他のプロキシ ノードの場合と同様に、新しいアトリビュートの追加および設定を行うには initialize() メソッドを使用します。次のサンプルでは、aRockInX アトリビュートがノードに追加され、キー設定可でワールド空間に作用(affects world space)に設定されます。また、アトリビュートが変更されたときに Maya が正しく更新されるように、mustCallValidateAndSet() がコールされます。

    //
    //    Node initialize method. We configure node
    //    attributes here. Static method so
    //    the *this pointer 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;
    }

クラスを使用するための標準的なメソッドは、次のとおりです。

    //
    // 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. Has 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();
        }
    }
    //
    // 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();
    }

validateAndSetValue() 仮想メソッドを使用すると、アトリビュートの入力が正しいことを確認できます。たとえば、アトリビュートがロックまたはクランプされているかどうかを確認する必要があります。この場合、このメソッドを使用するとロックおよびクランプを無視できます。次の実施例では、この手順が実行されています。

    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);
    }

MPxTransform ノードでは、ロックとクランプの確認にいくつかの仮想メソッドを利用できます。たとえば、回転を使用してロックとクランプを適切に処理するには、checkAndSetRotation()、applyRotationLimits()、applyRotationLocks() を使用します。他のアトリビュートにも、同じメソッドのセットが存在します。Maya API 開発キットの rockingTransformCheck のサンプルには、回転に関するこれらの原理が例示されています。