ジョイントの親に自動で作成される厄介者!?

2021/10/20

こんにちは。
尾形です。

ジョイントの階層を変更した際、ジョイントの親にTransformノードが作成されることがあります。
 

今回は、このTransformノードが作成されるタイミング回避方法についてご紹介します。
※Mayaのお話です。作成される理由については別の機会にご紹介します。

 


0.アトリビュート値の桁数

本題に入る前に、ChannelBoxに表示されるアトリビュート値の表示桁数について簡単にお話します。

アトリビュートに表示される値は、デフォルト設定では小数第三位までが表示されています。
この桁数は、
ChannelBoxメニューの  Edit > Settings > Change Precisionで変更できます。

 

例えば、キューブを作成してマニュピレータでX軸方向へ移動させると小数第三位までの表示ですが、

Change Precisionで桁数を10に変更すると小数第十位まで表示されます。

 

-0」という表示を見たことはないでしょうか?
これも表示桁数を変更することで、マイナスがついている理由を確認することが出来ます。

Change Precisionが3の場合、一見0に見えますが…

Change Precisionを10に変更して確認すると…

.000123という細かい値が設定されていました。

このように、一見「0」や「1」のような整数に見えても、小数点以下に細かい値が設定されていることがあります。
リグでスケールを扱う際には、こういった細かい値が問題になる場合があります(後述)。
表示されている桁数や小数点以下の値に注意して作業するようにしましょう。

 


1.作成されるタイミング

では、ジョイントの階層を変更した際に、ジョイントの親にTransformノードが作成されるのはどういったタイミングでしょうか?

 

1.Scaleの問題

ScaleX/Y/Zに1以外の値が設定されている(親)ノード階層から出す、または入れる時に作成される。

先ずは階層から出す例で確認してみます。

例)階層から出す
joint1のScaleXには「1.5」が設定されています。

joint2をjoint1の階層の外に出してみると…

joint2の親に自動的にTransformノードが作成され、
Transform1のアトリビュートにはjoint1と同じ値が設定されました。

 このように、親階層のノードのScaleX/Y/Zに1以外の値が設定されている状態でジョイントを階層の外に出すと、
自動的にジョイントの親にTransform
ノードが作成されます。
inverseScaleアトリビュートを表示させて確認すると原理が理解できるかと思います。

 

次に、階層に入れる例で確認してみます。

例)階層に入れる
joint1のScaleXには「1.5」が設定されています。

joint2は任意の位置に配置されています。
ScaleX/Y/Zには「1」が設定されています。

joint2をjoint1の階層に入れてみると…

自動的にjoint2の親にTransformノードが作成され、
Transform1のアトリビュートにはよく分からない値が設定されました。
Pivotの位置は原点のようです。

joint2のアトリビュートには、joint1の階層に入れる前と同じ値が設定されています。

このように、ノードのScaleX/Y/Zに1以外の値が設定されている階層にジョイントを入れると、
自動的にジョイントの親にTransformノードが作成されます。

 

2.Shearの問題

「ShearXY/XZ/YZ に0以外の値が設定されているノード」階層から出す、または入れる時に作成される。

Shearの値は、Windows > General Editors > ChannelControlでChannelBoxに表示することができます。

上述の「1.Scaleの問題」と同様に試してみて下さい。

 

3.InverseScaleの問題

「InverseScaleX/Y/Z に1以外の値が設定されているノード」階層から出す、または入れる時に作成される。

Shearと同様、ChannelBoxに表示されていないので見落としがちですが、
ジョイントのInverseScaleに値が設定されていることがあります。

例)階層から出す
joint1のInverseScaleXには「1.5」が設定されています。

joint2をjoint1の外に出してみると…

joint2の親に自動的にTransformノードが作成され、
Transform1のTranslateとRotateにはjoint1と同じ値が設定されました。
ScaleZには「1」
÷「1.5」の値が設定されました。

このように、親階層のノードのInverseScaleX/Y/Zに1以外の値が設定されている状態でジョイントを階層の外に出すと、
自動的にジョイントの親にTransformノードが作成されます。
ScaleやShearで問題が解決しない場合は、InverseScaleの値も確認してみましょう。

 

例)階層に入れる

同様なので割愛します。

 

4.小数点以下の細かい値の問題

「ScaleX/Y/Z(1.0)、ShearXY/XZ/YZ(0.0)、InverseScaleX/Y/Z(1.0)の小数点以下に細かい値が設定されているノード階層から出す、または入れる時に作成される。

冒頭で説明した通り、一見「0」や「1」のような整数に見えても、小数点以下に細かい値が設定されている場合があります。

 

ストレッチ機能の例で確認してみます。

下動画のようなストレッチ機能を設定する際、
各ジョイントのScaleの子供の方向の軸(動画ではX軸)に
「SplineIKカーブの長さ」÷「初期ポーズでのジョイントの根元から末端までの長さ」
の値を接続するといった手法があります。

下図のように極めてシンプルなリグです。

簡単に説明しますと、
1. ジョイントの根元から先端にかけてSplineIkを設定します。
2.
curveInfoノードを作成して、SplineIkのcurveShapeのLocalとcurveInfoのInputCurveを接続します。
3. multiplyDivideノードを作成して、curveInfoのArcLengthとmultiplyDivideのInput1Xを接続します。
4. AttributeEditorでmultiplyDivideのInput1Xの値をコピーしてInput2Xにペーストします。
 
5. multiplyDivideのOperationをDivideに変更します。
6. multiplyDivideのOutputXを各ジョイントのScaleXに接続します。

 

さて、ここで4に注目します。
multiplyDivideのInput2Xには、Input1Xの値をコピー&ペーストして入力しました。
AttributeEditorでは、一見Input1XとInput2Xの値が同じ「12.170」に見えます。

ChannelBoxでChange Precisionを10に変更して確認すると…

小数第二位以降の値が微妙に違うことが確認できます。

 このmultiplyDivideの結果が入力されるジョイントのScaleXの値を確認すると…

1ではなく「0.9999915957」という細かい値が設定されていることが確認できます。

Change Precisionを3に変更して確認すると…

「1」と表示されます。

ジョイントを階層から外に出すと…

親階層のジョイントのScale値が「1」ではないため、joint2の親に自動的にTransformノードが作成されました。

このように、コピー&ペーストで値を設定すると誤差が発生する可能性があるので注意が必要です。

 


2.回避方法

基本的には、該当するノードのScale、Shear、InverseScaleの値を正規化することで、Transformノードの作成を防ぐことが出来ます。
値のコピー&ペーストの問題については、
PythonコマンドのgetAttr(値を取得)とsetAttr(値を設定)を使用すると、
小数点以下の桁に依存することなく、値をコピー&ペーストすることができます。
※melコマンドの場合は小数第六位までしか扱えないため誤差が発生する可能性があります。 

上記のストレッチ機能の例では、

または

となります。

ChannelBoxで確認すると…

同じ値であることが確認できます。

 

値をコピー&ペーストする際は、Pythonコマンドが便利です。

 


まとめ

概念を理解するまでは、自動でTransformノードが作成される処理が厄介に感じていたのですが、
概念を理解した今では、親ノードにスケール値が設定されているかを検出する手段として使用しています。

Scaleの扱いは非常に難しくまた問題になることも多いので、小数点以下の値にも注意しましょう。
この記事が皆さんのご参考になれば幸いです。
ではまた!

 

※免責事項※
本記事内で公開している全ての情報について、その完全性、正確性、適用性、有用性等いかなる保証も行っておりません。
これらの情報のご利用により、何らかの不都合や損害が発生したとしても、当社は何らの責任を負うものではありません。
自己責任でご使用ください。