ノード変換プロパティの使い方

このトピックでは、ノードに関連する 3 つの変換であるノード変換行列、オブジェクト オフセット変換行列、およびオブジェクト変換行列について説明します。ノード変換行列とオブジェクト オフセット変換行列は、3ds Max によって保存されます。オブジェクト変換行列は、これらの 2 つの変換行列から派生します。このトピックでは、これらの変換の生成方法、3ds Max での変換の使用方法、MAXScript での変換へのアクセス方法および操作方法についても説明します。

基点 — ノード変換行列

変換の中心となる基点とは、回転が実行される位置、またはスケールの開始および終了位置のことです。3ds Max 内のすべてのノードには、基点があります。基点は、ノードのローカルの中心、およびローカル座標系として考えることができます。ノードの基点は、以下のような目的に使用されます。

  • 回転およびスケールの中心位置

  • リンクされた子の変換原点の定義

  • インバース キネマティックの結合位置の定義

多くのユーザは、基点とはノードが選択され、座標系がローカルに設定されたときに 3ds Max に表示される 3 本の軸であると考えていますが、実際には、これは単にノードの変換行列を視覚的に示したものです。

ノードの変換行列は、ノードをシーンに配置する変換コントローラによってコントロールされます。変換コントローラでは、ノードの親に関連する変換がコントロールされます。

PRS 変換コントローラにおけるノード変換行列の構造

PRS コントローラではサブコントローラを使用して変換を作成します。このサブコントローラには、位置、回転、スケール用の 3 つがあります。PRS コントローラにはノードの親の変換行列が渡されます。ノードに親がない場合、その行列は単位行列となります。PRS コントローラは、最初に位置コントローラを呼び出し、次に回転コントローラ、スケール コントローラの順に呼び出します。最初に、位置コントローラでは入力行列に位置の値が乗算されます。渡された行列が単位行列の場合、この処理は行列の最下行(変換行)を設定することと同じになります。次に、回転コントローラで行列に回転の値が乗算されます。最後に、スケール コントローラで行列にスケールの値が前処理として乗算されます。

位置コントローラによっては、位置以外の値を行列に適用することもできます。たとえば、パス コントローラの場合、フォロー スイッチがアクティブであると、従うパスの接線方向にノードを残したまま回転が適用されます。つまり、回転コントローラが行列を受け取るとき、行列には既に回転が適用されていることになります。

位置、回転、スケール コントローラが行列を更新すると、その行列は 3ds Max がシーンでノードの位置設定、回転、およびスケールを実行するために使用するノード変換行列になります。このため、変換全体は次のように表すことができます。

ノード変換行列 = コントローラのスケール * コントローラの回転 * コントローラの位置 * 親変換行列

オブジェクト オフセット変換行列

オブジェクト オフセット変換行列は、そのノードからオブジェクトのジオメトリのオフセットを提供します。

ノードを選択して[階層](Hierarchy)パネルの階層分岐に進み、[基点](Pivot)ボタンを選択して[基点にのみ影響](Affect Pivot Only)ボタンまたは[オブジェクトにのみ影響](Affect Object Only)ボタンを選択すると、ノードの基点を 3ds Max で表示できます。これにより、ノードの基点の位置と方向を示す 3 本の軸が大きく表示されます。これらのボタンのいずれかをクリックし、[移動](Move)/[回転](Rotate)/[スケール](Scale)のコントロールを使用すると、基点に依存しないオブジェクトのジオメトリの位置、またはオブジェクトのジオメトリに依存しない基点を操作することができます。

ユーザが基点やオブジェクトを個別に操作できるように、基点とオブジェクトはオブジェクト オフセット変換行列を使って内部的に管理されています。オブジェクト オフセット変換行列は、オブジェクトのジオメトリとノードの関係に影響します。オブジェクト オフセット変換行列は、ある程度のオブジェクト自体のジオメトリをノードから変換します。

オブジェクト オフセット変換行列の使い方を理解するには、以下の 3ds Max ユーザ インタフェースの例を参照してください。[基点](Pivot)の下に表示される階層分岐で[オブジェクトにのみ影響](Affect Object Only)ボタンをクリックした場合、ノードに依存せずにオブジェクトのジオメトリを自由に移動できます。基点に対するオブジェクトのジオメトリの方向だけは設定できます。オブジェクト オフセット変換行列は、内部的に操作されます。この変換は、ノードに依存しないオブジェクトのジオメトリに適用される追加オフセットです。オブジェクト オフセット変換行列は、子ノードに継承されません。

別の例として、[基点にのみ影響](Affect Pivot Only)ボタンをクリックする場合があります。このモードでは、オブジェクトのジオメトリの位置を変えずに、基点を移動できます。基点を移動すると、それに伴ってノード変換行列が変更され(基点の方向が更新される)、オブジェクト オフセット変換行列はノード変換とは逆方向に調整されます。基点をどこに移動しても、オブジェクトのジオメトリはそのまま同じ位置に存在することになります。

オブジェクト オフセット変換行列の構造

オブジェクト オフセット変換行列は、個別の位置変換、回転変換、およびスケール変換で構成されています。ノード変換行列と同様に、これらの変換には、位置、回転、スケールの順で事前に乗算が実行されます。このため、オブジェクト オフセット変換行列は、次のように表すことができます。

オブジェクト オフセット変換行列 = オフセットのスケール * オフセットの回転 * オフセットの位置

ノード変換行列とは異なり、オブジェクト オフセット変換行列はノードの子に継承されません。

オブジェクト変換行列

オブジェクト変換行列は、オブジェクトのジオメトリをワールド空間に変換するときの乗算に必要な変換行列です。この変換行列には、親の変換、ノード変換、およびオブジェクト オフセット変換が含まれます。このため、任意のオブジェクトのポイントを変換するために使用される変換全体は、次のように表すことができます。

オブジェクト変換行列 = オブジェクト オフセット変換行列 * ノード変換行列

たとえば、この行列は、メッシュ オブジェクトがある場合に、この頂点のうちの 1 つのワールド空間座標を取得する場合などに使用できます。この操作は、オブジェクト空間内で頂点の座標を取得し、それにオブジェクト変換行列を乗算することで実行できます。

MAXScript でのノード変換の使い方

ノード変換プロパティは、ノードと関連付けられた変換コントローラの値から派生し、この値を修正します。ノード変換プロパティは、ノードのメイン変換行列とは直接関係ありません。これは、以下のことを意味します。

  1. 変換プロパティ( pos rotation )に有効なアニメーションを割り当てると、関連付けられたコントローラ内でのみキーフレームを配置できます。ノードの変換行列を直接修正する場合は、そのノードのすべての変換コントローラについてキーフレームが生成されます。

  2. 回転プロパティと MAXScript における回転に関連するすべての関数に、3ds Max ユーザ インタフェースで使われている方向規則が適用されます。この規則とは、右手規則です。つまり、正の角度では正の軸を反時計回りに回転します。内部的には 3ds Max左手規則 を使用して回転をノード行列に保存します。 transform および objectTransform プロパティを使用して、直接ノード変換行列の作業をするときは、この逆転を覚えておくことが重要になります。3ds Max の回転と MAXScript の標準の回転を両方使用するときは、回転を逆にする必要があります(たとえば、軸に[-1,-1,-1]を乗算したり、または Quaternion で inverse() 関数を使用する)。

  3. バンクやフォローが有効になっているパス コントローラなど、一部のコントローラは、ノードの変換行列を直接調整してノードの回転に影響を与えます。また、この回転は回転コントローラの値に反映 されません。これは、このインスタンスの例では、バンク回転やフォロー回転が rotation プロパティから直接操作できないことを意味します。次のようにして、ノードの変換行列内の回転へ直接アクセスする必要があります。

    r = $foo.transform.rotationPart
    

    これは Quaternion を返します。変換行列を直接操作するときは 3ds Max の回転方向規則が適用されないことに注意してください。 この値を他の回転操作で使用するには、回転を逆にする必要があります。

    $baz.rotation = inverse r
    

    ノードの transform プロパティには、ノード変換行列が含まれます。また、このプロパティは、ノードの基点の位置、回転、およびスケールを反映します。 pivot プロパティにアクセスすると、 pos プロパティと同じ値が返されます。 pivot プロパティを設定すると、基点の位置は指定した座標に移動されますが、ノードのジオメトリは移動されません。

    ノードの objectoffsetpos objectoffsetrot 、および objectoffsetscale プロパティには、オブジェクトオフセット変換行列のコンポーネント部分が含まれます。ノードの objecttransform プロパティには、オブジェクト変換行列が含まれます。この行列は、ノード変換行列とオブジェクト オフセット変換行列を組み合わせたものであるため、プロパティは読み込み専用になります。これらのプロパティは、常にワールド座標系コンテキストで返されることに注意してください。

    スクリプト:

    fn DumpXForms obj =
    (-- output node transform properties
    format "%:\t%\n" "transform" obj.transform
    format "%:\t%\n" "position " obj.pos
    format "%:\t%\n" "rotation " obj.rotation
    -- output node's pivot point location
    format "%:\t%\n" "pivot " obj.pivot
    -- output object offsets
    format "%:\t%\n" "objectoffsetpos" obj.objectoffsetpos
    format "%:\t%\n" "objectoffsetrot" obj.objectoffsetrot
    format "%:\t%\n" "objectoffsetscale" obj.objectoffsetscale
    -- output object transform
    format "%:\t%\n" "objecttransform " obj.objecttransform
    -- output vertex position in local and world coordinates
    format "%:\t%\n" "vert 1 (local) "(in coordsys local getvert obj 1)
    format "%:\t%\n" "vert 1 (world1) "(in coordsys world getvert obj 1)
    -- calculate and output vertex position in world coordinates
    local v_pos = (in coordsys local getvert obj 1)* obj.objecttransform
    format"%:\t%\n" "vert 1 (world2) " v_pos
    )
    -- define function for rotating only the pivot point
    fn RotatePivotOnly obj rotation = (local rotValInv=inverse (rotation as quat)
    animate off in coordsys local obj.rotation*=RotValInv
    obj.objectoffsetpos*=RotValInv
    obj.objectoffsetrot*=RotValInv
    )
    --
    (
    b=box pos:[75,75,0]-- create a 25x25x25 box, vertex 1 at [62.5,62.5,0] (world)
    convertToMesh b-- convert box to mesh so we can access the vertex location
    DumpXForms b-- print transforms
    b.pivot=[50,50,0]-- move pivot only to [50,50,0]
    DumpXForms b-- print transforms
    RotatePivotOnly b (EulerAngles 0 0 35)-- rotate pivot only 35 degrees about local Z
    DumpXForms b-- print transforms
    )
    

    出力

    DumpXForms()-- function definition
    RotatePivotOnly()-- function definition
    transform: (matrix3 [1,0,0], [0,1,0], [0,0,1], [75,75,0])-- initial transforms
    position : [75,75,0]
    rotation : (quat 0 0 0 1)
    pivot : [75,75,0]
    objectoffsetpos : [0,0,0]
    objectoffsetrot : (quat 0 0 0 1)
    objectoffsetscale: [1,1,1]
    objecttransform : (matrix3 [1,0,0], [0,1,0], [0,0,1], [75,75,0])
    vert 1 (local) : [-12.5,-12.5,0]
    vert 1 (world1) : [62.5,62.5,0]
    vert 1 (world2) : [62.5,62.5,0]
    transform: (matrix3 [1,0,0], [0,1,0], [0,0,1], [50,50,0])-- transforms after move
    position : [50,50,0]
    rotation : (quat 0 0 0 1)
    pivot : [50,50,0]
    objectoffsetpos : [25,25,0]
    objectoffsetrot : (quat 0 0 0 1)
    objectoffsetscale: [1,1,1]
    objecttransform : (matrix3 [1,0,0], [0,1,0], [0,0,1], [75,75,0])
    vert 1 (local) : [-12.5,-12.5,0]
    vert 1 (world1) : [62.5,62.5,0]
    vert 1 (world2) : [62.5,62.5,0]
    transform: (matrix3 [0.819152,0.573576,0], [-0.573576,0.819152,0], [0,0,1], [50,50,0])-- transforms after rotate
    position : [50,50,0]
    rotation : (quat 0 0 0.300706 0.953717)
    pivot : [50,50,0]
    objectoffsetpos : [34.8182,6.13939,0]
    objectoffsetrot : (quat 0 0 0.300706 0.953717)
    objectoffsetscale: [1,1,1]
    objecttransform : (matrix3 [1,0,0], [0,1,0], [0,0,1], [75,75,0])
    vert 1 (local) : [-12.5,-12.5,0]
    vert 1 (world1) : [62.5,62.5,0]
    vert 1 (world2) : [62.5,62.5,0]