マニピュレータとノードのやり取り

マニピュレータはノードのプラグとやり取りして、プラグの値を設定します。また、マニピュレータは、プラグの値に関連するマニピュレータ値も設定します。

マニピュレータとノードのやり取りは、単純な 1 対 1 の関係、または複雑な変換関数を使用して実行されます。以下は、ノードとマニピュレータのやり取りを説明する図です。

コンバータは、ノードのプラグとマニピュレータ値のやり取りを管理します。次の図の矢印は、情報が流れる方向を示しています。それぞれのコンテナ マニピュレータには 1 つのコンバータがあり、子マニピュレータとその影響を受けるプラグの間のインタフェースになります。

正方形のボックスは、コンバータとベース マニピュレータのデータ項目を示します。子マニピュレータの値に関連するコンバータの項目は converterManipValue 項目と呼ばれます。ノードのプラグ値に関連するコンバータの項目は converterPlugValue 項目と呼ばれます。

ベース マニピュレータの項目は manipValue 項目と呼ばれます。一部の manipValue 項目は、マニピュレータの可動性に直接関係します。たとえば MFnDiscManip::angleIndex は、DiscManip の回転可動性に直接関係します。その他の manipValue 項目はマニピュレータの可動性に関係しませんが、MFnDiscManip::centerIndex()MFnDiscManip::axisIndex() など、マニピュレータの位置や向きに関する重要な情報を提供します。

それぞれの converterManipValue 項目と converterPlugValue 項目には整数インデックスがあり、項目が一意に識別されるようになっています。ベース マニピュレータのそれぞれの manipValue 項目にも整数インデックスがあり、項目が一意に識別されるようになっています。

注: converterManipValue 項目とベース マニピュレータの manipValue 項目に 1 対 1 で対応しており、これらの項目は同じ整数インデックスを共有します。converterPlugValue 項目と、マニピュレータが影響するノードのプラグにも 1 対 1 の対応があります。

図から分かるように、converterManipValue 項目と converterPlugValue 項目の 1 対 1 の関係は直接的です。

converterManipValue 項目と converterPlugValue 項目の複雑な変換は、変換関数を通して実行されます。この関数では、任意の数の converterPlugValue 項目か converterManipValue 項目を使用し、対応する converterManipValue 項目か converterPlugValue 項目を計算します。

1 対 1 の関係

1 対 1 の関係では、マニピュレータ値がプラグ値と同期されます。つまり、マニピュレータ値はプラグ値と等しくなり、プラグ値はマニピュレータと等しくなります。

converterManipValue 項目と converterPlugValue 項目間の 1 対 1 の関係は、MFnManip3D から派生したマニピュレータ クラスのメソッドを使用して確立されます。これらのメソッドと、それらのメソッドが接続するプラグに対応するデータ型は以下のとおりです。

これらのメソッドは、connectToDependNode() メソッドから呼び出す必要があります。たとえば footPrintManip の例では以下のようになっています。

MStatus footPrintLocatorManip::connectToDependNode(const MObject &node)

{
    ...
    MFnDistanceManip distanceManipFn(fDistanceManip);
    MFnDependencyNode nodeFn(node);
    MPlug sizePlug = nodeFn.findPlug("size", &stat);
    if (MStatus::kFailure != stat) {
        distanceManipFn.connectToDistancePlug(sizePlug);
        ...
        finishAddingManips();
        MPxManipContainer::connectToDependNode(node);
    }
    return stat;
} 

次の図は、距離マニピュレータ値とノードのサイズ プラグの値の 1 対 1 の関係を示しています。

1 対 1 の関係を設定した後、マニピュレータ値が変更されるたびに対応するプラグ値も変更され、またプラグ値が変更されるたびに、マニピュレータ値も変更されます。

1 対 1 の関係は、マニピュレータ値とプラグ値の関係を設定するのに便利な方法です。ただし、この方法は、より複雑な要件では使用できない場合があります。たとえば、距離マニピュレータ値を、サイズの値の 5 倍に設定することはできません。また、ノードを移動するときに、距離マニピュレータはノードの中心に表示されず、ノードの位置に沿ってマニピュレータを移動することはできません。マニピュレータの位置がオブジェクトの位置の影響を受ける必要がある場合、またはマニピュレータのグループを特定の方法で一緒に移動する必要がある場合は、変換関数を使用する必要があります。

変換関数

変換関数は、マニピュレータ値とプラグ値との変換に使用します。これらはコールバック メソッドとして実装されています。変換関数は、マニピュレータ値とプラグ値との複雑な関係を設定するために使用します。

変換関数を使用するマニピュレータの簡単な例としては、プラグに接続された DiscManip (タイプ MFnUnitAttribute::kAngle のアトリビュートに関連します)を持つコンテナ マニピュレータがありますが、これは disc マニピュレータの回転を取り、その回転を 10 倍にします。変換関数は、MFnDiscManip::angleIndex()MPxManipContainer::getConverterManipValue() を使用して、その角度を 10 倍にします。

変換関数は、マニピュレータの位置がオブジェクトの位置に影響される場合、またはマニピュレータのグループを特定の方法で一緒に移動する場合に非常に便利です。変換関数を使用しないと、マニピュレータを 1 単位として一緒に移動できず、マニピュレータの特定コンポーネントは空間の原点か固定位置に残ります。

注: FreePointTriadManip で位置を指定する場合、または PointOnCurveManip でカーブに沿ったパラメータを指定する場合、変換関数は必要ありません。PointOnCurveManip とともに移動する DiscManip がある場合は、変換関数を使用して、位置と法線に関する情報を DiscManip に与える必要があります。

変換コールバック メソッドには、plugToManip と manipToPlug の 2 種類があります。

変換コールバック: plugToManip

plugToManip 変換コールバックは、さまざまな converterPlugValue 項目から converterManipValue 項目の値を取得するために使用します。このコールバックは、すべての converterPlugValue 項目にアクセスし、converterManipValue 項目の値を返します。

PlugToManip 変換は、MPxManipContainer::addPlugToManipConversionCallback() を呼び出して実装されます。

それぞれの表示コントロール プロパティには、マニピュレータに登録されている、対応するインデックスがあります。plugToManip の変換関係を設定するには、MPxManipContainer::addPlugToManipConversionCallback() を呼び出すときに、マニピュレータ値のインデックスを渡す必要があります。各ベース マニピュレータ関数セット クラスには、個々のプロパティの異なるインデックスを取得するための関数があります。

footPrintManip の次のコードは、マニピュレータの開始地点のインデックスを取得し、そのインデックスを addPlugToManipConversionCallback() 関数呼び出しに渡す方法を示しています。この関数呼び出しの 2 つ目のパラメータはコールバック変換関数で、いくつかのプラグ値に基づいて開始地点のマニピュレータ値を計算します。

unsigned startPointIndex = distanceManipFn.startPointIndex();
addPlugToManipConversionCallback (startPointIndex,(plugToManipConversionCallback)&footPrintLocatorManip::startPointCallback);

PlugToManip 変換コールバックは、プラグ値にアクセスし、カスタム アルゴリズムに基づいてマニピュレータ値を計算して、その値を返します。この例では、コールバック関数はプラグ値を要求せず、代わりにワールド空間でノード移動を取得し、この値を返します。このようにすることで、実際に開始地点のマニピュレータ値がノード移動と同じになるように設定されます。結果は、ノードが移動するたびに、マニピュレータはノードに合わせて移動し、常にノードの中央に表示されます。

MManipData footPrintLocatorManip::startPointCallback(unsigned index) const {
    MFnNumericData numData;
    MObject numDataObj =numData.create(MFnNumericData::k3Double);
    MVector vec = nodeTranslation();
    numData.setData (vec.x, vec.y, vec.z);
    return(MManipData (numDataObj));
}

変換コールバックは、MManipData と呼ばれるデータ型を返し、マニピュレータ値を MManipData が表す値に設定します。

変換コールバック: manipToPlug

manipToPlug 変換コールバックは、さまざまな converterManipValue 項目から converterPlugValue 項目の値を取得するために使用します。このコールバックは、すべての converterManipValue 項目にアクセスし、converterPlugValue 項目の値を返します。

manipToPlug 変換は、MPxManipContainer::addManipToPlugConversionCallback() を呼び出して実装されます。コールバックは変更するプラグ値を認識している必要があるため、対応するプラグは最初のパラメータとして渡す必要があります。

rotateManip の例の次のコードは、渡されたノードの回転プラグでこの手法を使用する方法を示しています。

MFnDependencyNode nodeFn (node);
MPlug rPlug =nodeFn.findPlug ("rotate", &stat);
…
rotatePlugIndex = addManipToPlugConversionCallback(rPlug,(manipToPlugConversionCallback)&exampleRotateManip::rotationChangedCallback);

addManipToPlugConversionCallback() 関数呼び出しは、このコールバックに登録されているプラグを識別するための、rotatePlugIndex インデックスを返します。実際のコールバック関数が呼び出されると、計算されるプラグ値のインデックスは、関数呼び出しのパラメータとして渡されます。次に、コールバック関数は、渡された値がこのコールバックに登録されているインデックスかどうかを確認します。このコールバックに複数のプラグが登録されている場合は、条件ステートメントを使用して、渡された異なるプラグ インデックスを識別し、対応するマニピュレータ値を計算することができます。次の rotationChangedCallback() の例では、渡されたインデックスが rotatePlugIndex と比較されています。これらのインデックスが同じである場合、回転マニピュレータ インデックスで getConverterManipValue() が呼び出されて、回転マニピュレータ値が取得されます。MManipData オブジェクトとして回転マニピュレータ値を返すことによって、このコールバック関数は、回転プラグ値を回転マニピュレータ値と同じ値に設定します。特定の要件に応じて、変換コールバックを使用して回転プラグ値と回転マニピュレータ値の間に、より複雑な関係を設定できます。

MManipData exampleRotateManip::rotationChangedCallback(unsigned index) {
    MObject obj =MObject::kNullObj;
    // If we entered the callback with an invalid index, print an error and
    // return.  Because, we registered the callback only for one plug, all
    // invocations of the callback must be for that plug.
    if (index != rotatePlugIndex) {
        MGlobal::displayError("Invalid index in rotation changed callback!");
        // For invalid indices, return vector of 0's
        MFnNumericData numericData;
        obj = numericData.create(MFnNumericData::k3Double);
        numericData.setData (0.0, 0.0, 0.0);
        return (obj);
    }
    MFnNumericData numericData;
    obj =numericData.create(MFnNumericData::k3Double); 
    MEulerRotation manipRotation;
    if (!getConverterManipValue(rotateManip.rotationIndex(),manipRotation)) {
        MGlobal::displayError("Error retrieving manip value");
        numericData.setData(0.0, 0.0, 0.0);
    } else {
        numericData.setData(manipRotation.x, manipRotation.y, manipRotation.z);
    }
    return MManipData(obj);
}

一般的に、manipToPlug 変換は使用されません。converterPlugValue と converterManipValue の使用に加えて、DAG パスなどのクラスデータを使用すると便利なことがあります。fNodePath を使用してノード変換を計算する方法の例については、footPrintManip を参照してください。コンポーネント上で操作するマニピュレータについては、最初のコンポーネントの位置を保存しておくのも便利かもしれません(この方法の例については、componentScaleManip を参照)。

MManipData

変換コールバック メソッドは、MManipData というデータ型を返します。MManipData では、マニピュレータ変換関数から返されたマニピュレータ データがカプセル化されます。これは、単純なデータか複雑なデータを表します。MManipData 上の単純なデータ メソッドは、bool、short、long、unsigned、float、double を表すために使用します。

注: 単純データ型に関連するアトリビュートが、距離、角度、時間など、高いレベルになることがあります(たとえば MFnUnitAttribute::kAngleMFnUnitAttribute::kDistanceMFnUnitAttribute::kTime)。

MManipData は、行列、カーブ、データの配列など、MFnData またはその派生クラスによって作成された複雑なデータ型を表すためにも使用します。

プラグインの footPrintManip には、startPointCallback という plugToManip 変換コールバックの例が含まれています。startPointCallback は MManipData を返し、これは MFnNumericData が作成する MObject に設定されています。

class footPrintLocatorManip : public MPxManipContainer
{
    public:
        ...
        MManipData startPointCallback(unsigned index) const;
        MVector nodeTranslation() const;
        MDagPath fDistanceManip;
        ...
};
MManipData footPrintLocatorManip::startPointCallback
    (unsigned index) const
{
    // The index is the startPointIndex that is
    // specified in addPlugToManipConversionCallback,
    // but it is not necessary to use this in the callback.
    MFnNumericData numData;
    MObject numDataObj =
        numData.create(MFnNumericData::k3Double);
    MVector vec = nodeTranslation();
    numData.setData(vec.x, vec.y, vec.z);
    return MManipData(numDataObj);
}
MStatus footPrintLocatorManip::connectToDependNode
    (const MObject &node)
{
    ...
    unsigned startPointIndex =
        distanceManipFn.startPointIndex();
    addPlugToManipConversionCallback(
        startPointIndex, 
        (plugToManipConversionCallback) startPointCallback);
    ...
}

ヒント

Python および C++ の変換関数: Python プラグインで、変換コールバックの登録に使用できる関数は、addManipToPlugConversion()addPlugToManipConversion() だけです。C++ プラグインの場合は、addPlugToManipConversionCallback()/addManipToPlugConversionCallback() 関数か addManipToPlugConversion()/addPlugToManipConversion() 関数を使用して変換コールバックを登録できます。

マニピュレータ インデックスとプラグ インデックス: 表示コントロールのプロパティには、マニピュレータ内に登録されているマニピュレータ インデックスがあります。たとえば、MFnDistanceManip::startPointIndex() は開始地点のインデックスを返し、MFnRotateManip::rotationIndex() は回転マニピュレータ値のインデックスを返します。plugToManip 変換コールバックで設定されるマニピュレータ値を指定するには、addPlugToManipConversionCallback() または addPlugToManipConversion() を呼び出すときにマニピュレータ インデックスを使用する必要があります。

プラグ インデックスは、addManipToPlugConversionCallback() および addManipToPlugConversion() で返され、manipToPlug 変換コールバックに登録されているプラグを確認します。

さらに、マニピュレータ インデックスを使用して、getConverterManipValue() 関数から返されるマニピュレータ値を取得し、プラグ インデックスを使用して、getConverterPlugValue() 関数からプラグ値を取得します。