スクリプト マニピュレータ

マニピュレータは、ヘルパー オブジェクトのタイプで、3D ビューポートでのパラメータの直接操作をサポートするように設計されています。マニピュレータを設定して、オブジェクト、モディファイヤ、コントローラ、ノードのパラメータを操作できます。

メイン ツールバーに[マニピュレータ](Manipulate)ボタンがあります。これを押すとシステムは、選択したオブジェクトのマニピュレータをすべて表示します。マニピュレータはマウスが通過するときに赤くなり、ツールチップ ポップアップ リストには、マニピュレータにより影響を受けるパラメータの現在の値の名前が表示されます。値を操作するには、左マウス ボタンでギズモをクリックしてから、ドラッグします。大半のマニピュレータは、できるだけギズモが現在のマウス位置の下を通るように設計されています。

注:「操作」は独立したモードではありません。現在のモード([選択](select)、[移動](move)、[作成](create)など)を変更してマニピュレータを表示し、操作を可能にします。たとえば、[移動](move)モードにいる場合、従来どおりにそのまま移動を実行できます。唯一異なるのは、マニピュレータをクリックすると、移動ではなく操作するという点です。

マニピュレータには 2 つの種類があります。1 つは、「操作」モードに入るときに自動的に作成されるマニピュレータです。ホットスポット マニピュレータがこれに相当します。もう 1 つの種類はスタンドアロン オブジェクトで、スライダ マニピュレータがこれに相当します。これらは通常、使い勝手を向上させるために、パラメータ ワイヤリングとともに使用されます。

スタンドアロン マニピュレータを作成するには、[作成](create)パネルの「ヘルパー」に移動して、ドロップダウン リストの「マニピュレータ」を選択します。

操作するためにスタンドアロン マニピュレータを選択する必要はありません。ただし、ホットスポット マニピュレータのように自動的に作成されるマニピュレータは、選択したオブジェクトに対してのみ表示されます。

マニピュレータは、3D ギズモと 2D(画面スペース)ギズモの両方をサポートします。3D ギズモの例としてホットスポット マニピュレータを、2D ギズモの例としてスライダを挙げることができます。

スクリプト マニピュレータ

マニピュレータは、標準の MAX プラグインとして C++ で作成するか、MAXScript に実装することができます。MAXScript では、これらはスクリプト プラグイン オブジェクトの 1 つのタイプです。このため、独自のマニピュレータを記述する場合は、まず最初に「スクリプト プラグイン」を読むことをお勧めします。これは、スクリプトで記述されたジオメトリ オブジェクトと非常によく似ています。

サンプル コードとして使用可能な 3 つのスクリプト マニピュレータが「¥stdplugs¥stdscripts」フォルダにあります。

半径マニピュレータ

半径マニピュレータはかなり単純なもので、スクリプト マニピュレータ システムを記述する場合の良い例になります。

このマニピュレータのコードには次のコードが含まれます。コードの後ろにどのように動作するか詳細に記載されています。

例:

--------------------------------------------------------------------
-- Generic radius manipulator
-- Written by Scott Morrison
-- This manipulator sets the radius on any object or modifier with
-- a parameter named "radius". It creates a circle gizmo of the appropriate
-- radius centered at the origin in the XY plane.
plugin simpleManipulator radiusManip
name:"RadiusManip"
invisible:true
(
-- Create the green and red colors for the gizmo
local g = [0, 1, 0], r = [1, 0, 0]
-- This manipulator manipulates any node with a "radius" property
on canManipulate target return (findItem (getPropNames target) #radius) != 0
-- Create the manipulator gizmo.
-- This is called initially and whenever the manipulator target changes
on updateGizmos do
(
-- Clear the current gizmo cache
this.clearGizmos()
-- Set the radius of circle gizmo a little bigger than the target radius
giz = manip.makeCircle [0,0,0] (target.radius * 1.01) 28
-- Add the circle to the manipulator
this.addGizmoShape giz 0 g r
-- return the ToolTip string
return node.name + " radius = " + target.radius as string
)
-- mouseMove is called on every mouse move when dragging the manip
-- It needs to convert the mouse position 'm' into a new value for the radius
on mouseMove m which do
(
-- Create the XY plane.
-- manip.makePlaneFromNormal takes a normal vector and a point
-- and creates a plane passing through the point with the given normal
local pl = manip.makePlaneFromNormal z_axis [0, 0, 0],
projectedPoint = [0,0,0]
-- Compute the hit-ray in local coordinates
viewRay = this.getLocalViewRay m
-- Intersect the plane with the view ray
res = pl.intersect viewRay &projectedPoint
-- If the intersection worked, set the radius
if (res) then target.radius = (length projectedPoint) / 1.01
)
)
-------------------------------------------------------------------- 

ヘッダ:

plugin simpleManipulator radiusManip
name:"RadiusManip"
invisible:true

MAXScript に、これが「RadiusManip」というスクリプト マニピュレータ プラグインであることを知らせます。「invisible:true」は、作成パネルでマニピュレータ用の作成ボタンを作らないことをシステムに伝えます。

本体:

各種イベント用ハンドラのセット。

on canManipulate target return (findItem (getPropNames target) #radius) != 0

canManipulate 」は、「操作」ボタンを押したときに選択されるノードごとに、各マニピュレータで呼び出されます。「 target 」パラメータは、操作する可能性のあるオブジェクトです。これは、基本オブジェクト、オブジェクト上のすべてのモディファイヤ、およびオブジェクトのノード上の変換コントローラで呼び出されます。また、変換コントローラが PRS コントローラの場合、位置、回転、およびスケール コントローラで「 canManipulate 」を呼び出します。

半径マニピュレータの場合は、「 radius 」というプロパティを持つオブジェクトを操作できます。

球のような特定のオブジェクト タイプを操作するマニピュレータを作成する場合は、次のスクリプトを使用します。

on canManipulate target return classOf target == sphere

「canManipulateNode n 」という、代替ハンドラが必要になる場合もあります。これは、選択したノードをハンドラに渡すたびに渡されます。これは通常は必要ではありませんが、「 canManipulate 」に「 target 」として渡されるもの以外のノードのプロパティをマニピュレータで操作する場合は有効です。

注:

スクリプト マニピュレータ プラグインは両方のハンドラを同時に実装できますが、結果として生じる動作はユーザを混乱させることがあります。必要なハンドラ 1 つだけを実装することをお勧めします。

次のハンドラは「 updateGizmos 」と呼ばれ、マニピュレータがギズモを作る必要があるとき常に呼び出されます。マニピュレータの作成時に操作中のターゲットが変わる場合は、常にこれが呼び出されます。

MAXScript は、マニピュレータの作成時に、ハンドラへの呼び出しの内部で有効な変数を設定します。その 1 つが「 target 」と呼ばれる変数で、操作対象のオブジェクト、モディファイヤ、またはコントローラになります。もう 1 つは「 this 」で、これはマニピュレータ自体のことです。これも、ギズモでフラグとして使用可能な定数値を設定します。詳細は、後で説明します。「 node 」値も使用できます。これはターゲットを含むノードです。

すべてのマニピュレータが「 updateGizmos 」ハンドラの最初に含める必要があるものは、「 this.clearGizmos() 」呼び出しです。これは現在キャッシュされているギズモをクリアします。

次に、ビューポートに表示されるギズモのセットを作成します。半径マニピュレータの場合は、操作する半径を表す単一の円を作成します。

giz =manip.makeCircle [0,0,0] (target.radius * 1.01) 28

manip 」は、マニピュレータから使用可能な、エクスポートされたユーティリティ インタフェース関数のセットです。詳細については、下記の参照を参照してください。この例では円を作成しています。円の中心は原点にあり、半径は操作する半径の 1.01 倍で、セグメントは 28 です。操作中のオブジェクトからギズモがほんの少しだけ突き出るように、係数「1.01」が加えられています。半径を直接使用すると、操作中のオブジェクトと同じ場所にあるため、ビューポートからは見えない場合があります。

3D ギズモは、マニピュレータのターゲットを含むノードの、ローカル座標系で定義されることに注意してください。マニピュレータの表示やヒットテスト実行時にノードを移動、回転、またはスケールすると、システムが自動的に補正を行います。

次に、マニピュレータにギズモを追加します。

this.addGizmoShapegiz 0 g r

これは、特殊なフラグ値を含めずに(つまり「0」で)シェイプ ギズモをマニピュレータに追加することを、マニピュレータに伝えます。 これらのフラグすべてについては、以下で説明します。また、このコマンドは、未選択のカラーとして緑(「g」)、選択済みのカラーとして赤(「r」)を指定しています。選択済みのカラーはマウスがその上を通過する際に使用され、未選択のカラーはマウスがその上にない場合に使用されます。最後に、このメソッドは文字列値を返します。

この文字列値は、マウスがギズモの上を通過する際にツール チップとして使用されます。

通常ギズモは、メッシュ、形状(ワイヤフレーム)、テキスト、およびマーカーで構成されます。詳細は、下記の「参照」で説明されています。

次のハンドラは、「 mouseMove m which 」と呼ばれます。これは、ターゲットの操作中、マウスを移動するたびに呼び出されます。「 m 」パラメータはマウス位置のスクリーン座標を保持し、「 which 」パラメータは、ドラッグ中のギズモを示すインデックスです。ギズモは、「 updateGizmos 」で作成された順に、0 で始まる番号が付けられます。

mouseMove ハンドラは、通常、マニピュレータの実装で最も扱いにくい部分です。できるだけ、マニピュレータ ギズモがマウスの下を通るように、操作中のパラメータ値を更新する必要があります。

3 次元空間に存在するマニピュレータの場合、これは通常「 ヒットレイ 」を計算することで実行されます。ヒットレイはマウス位置を通過する 3 次元空間内のレイで、ビューの方向に移動します。

次のように計算されます。

viewRay = this.getLocalViewRaym

このビュー レイはこの後いくつかの平面と交差し、その交差点を使用して新しいパラメータ値が計算されます。

半径マニピュレータの場合、ここで使用している平面は XY 平面です。これは、半径円がローカル座標スペースの XY 平面にあるためです。

次のように計算されます。

local pl = manip.makePlaneFromNormalz_axis [0, 0, 0],

これにより、法線が Z 軸である平面で原点([0,0,0])を通る、「pl」が作成されます。

これをビュー レイと交差させるため、平面上で「交差」演算を使用します。

projectedPoint= [0,0,0]
res =pl.intersect viewRay &projectedPoint

まず最初に、交差の結果のホルダに「 projectedPoint 」を設定します。戻り値「 res 」は、交差を実行したかどうかを示すブール値です。true であれば交差は実行済み、false であれば失敗であることを示します。平面がビュー レイと平行している場合は、失敗することがあります。

projectedPoint 」にいったん交差点ができたら、それを使って半径の新しい値を決定します。このレイでは、新しい半径として原点(「 length projectedPoint 」)からの距離を使用します。操作中の実際の半径より 1.01 倍大きいギズモを作成したため、これを補正するために 1.01 で除算してスケールします。

これで操作は終了しました。通常は、「mouseMove」ハンドラで三角関数と線形代数を使う必要があります。直接操作を行うのは、ギズモはマウスの下を直接通る必要があるという考え方からです。

より複雑な 3D マニピュレータの例については、 UVWManip.ms のコードを確認してください。

スタンドアロン マニピュレータ

スタンドアロン マニピュレータは 2D スライダに類似していますが、多少余分な作業が必要です。

この説明では SliderManip.ms のコードを使用します。

ヘッダには、いくらか多くの情報が含まれます。

plugins simpleManipulator sliderManipulator
name:"Slider"
classID:#(0x47db14ef, 0x4e9b5990)
category:"Manipulators"

スタンドアロン マニピュレータは MAX ファイルに保存可能であるため、クラス ID が必要です。詳細は、『MAXScript リファレンス』を参照してください。ここにも「 invisible:true 」行は存在しません。これは、システムが「 Create 」パネルにこのボタンを作成することを意味します。これはヘルパー作成パネルの「 Manipulators 」セクションに配置されます。

また、オブジェクト用のパラメータ ブロックおよびロールアウト ユーザ インタフェースを定義する必要があります。定義は、他のスクリプト プラグインと同じ方法で行います。

スタンドアロン マニピュレータはそれ自体を操作するため、「 canManipulate 」ハンドラは次のようになります。

on canManipulate target return (classOf target) == sliderManipulator

さらに、マニピュレータ作成時にマウス操作を処理する作成ツールを提供する必要があります。これは他のスクリプト プラグインとまったく同じハンドルです。

スタンドアロン マニピュレータも「 updateGizmos 」ハンドラに、特殊コードを含める必要があります。スライダの場合は次のようになります。

-- If this is not a standalone manip, get values from the manip target
if (target != undefined) then
(
this.value = target.value
this.minVal = target.minVal
this.maxVal = target.maxVal
this.xPos = target.xPos
this.yPos = target.yPos
this.width = target.width
this.hide = target.hide
this.sldName = target.sldName
this.snapToggle = target.snapToggle
this.snapVal = target.snapVal
unselColor = greenColor
)
else
(
unselColor = yellowColor
)

target != undefined 」は、これがマニピュレータかスタンドアロン オブジェクトかを確認するためのテストです。オブジェクトがスタンドアロンのとき、「 target 」の値は未定義で、それ自体のパラメータ ブロックから取得したパラメータ値が使われます。target が定義済みであれば、操作中であることを意味します。その場合は、操作しているターゲットからパラメータの値をコピーします。

参照

スクリプト マニピュレータは次のハンドラを保持できます。

on canManipulate target

これは、このマニピュレータが指定したターゲットを操作するかどうかを示す値を返します。

on canManipulateNode n

これは、このマニピュレータが指定したノードを操作するかどうかを示す値を返します。

注:

マニピュレータはこれらのハンドラのうち、1 つだけしか実装しません。

on updateGizmos

これは、マニピュレータがギズモを構築する必要がある場合に、常に呼び出されます。これは、ツール チップ用に使われる文字列値です。空の文字列が返される場合、ツール チップは表示されません。

on mouseMove m which

これは、ユーザがギズモをつかんでそれをドラッグする場合に呼び出されます。

m 」パラメータはマウスのスクリーン座標を保持し、「which」パラメータは、ドラッグされているギズモの 0 で始まるインデックスを示します。これが、実際の操作を処理するものです。

注:

通常は、マウス位置に基づいて操作対象のパラメータ値を設定します。

on mouseDown m which

ユーザがギズモ上で最初にマウスをクリックするときに呼び出されます。

on mouseUp m which

操作の実行後に、ユーザがマウスを放したときに呼び出されます。

ヘルパー関数

マニピュレータがサポートするいくつかの組み込み関数、およびユーティリティ関数を含む 2 つのヘルパー パッケージがあります。

simpleManipulator タイプ自体が、これらの利用可能な関数を保持します。

this.clearGizmos()

これは、現在のギズモ キャッシュをクリアするために「updateGizmos」ハンドラの先頭で呼び出す必要があります。

this.addGizmoMesh mesh flags unselColor selColor

これによって、メッシュ(MAXの用語ではジオメトリ)からギズモが作成されます。メッシュはどの MAX メッシュでもよく、MAXScript のメッシュ作成用のツールで作成されていてもかまいません。簡単な方法の 1 つとしては、プリミティブのインスタンスを作成し、メッシュを作成する方法があります。

フラグには、0 を設定したり、1 つまたは複数の値を設定したりすることができます。複数のフラグを適用するには、各フラグの値を加算します。

次に、すべてのフラグ、および addGizmoText、addGizmoMarker、addGizmoShape、addGizmoMesh で使用可能かどうかを示します。

gizmoDontDisplay

ギズモを表示しないようにシステムに指示します。ヒット テストが行われますが、表示はされません。すべてに適用されます。

gizmoDontHitTest

ギズモをヒットテストしないようにシステムに指示します。ヒット テストが行われますが、表示はされません。すべてに適用されます。

gizmoActiveViewportOnly

アクティブなビューポートの該当するギズモの表示とヒット テストだけを行うようシステムに通知します。

this.addGizmoShapegizmo Shape flags unselColor selColor

これによって、シェイプ オブジェクトからギズモが作成されます。gizmoShape は、次に説明するように、「manip」パッケージの関数を使って作成できます。

フラグには「addGizmoMesh」と同じ値をセットできるほか、さらに 2 つの値がサポートされます。

gizmoUseScreenSpace

シェイプの座標を 3 次元の値ではなく、ビューポートのデバイス座標として解釈するようにシステムに指示します。値は 3 次元座標として引き続き指定されますが、「Z」座標は無視され、addGizmoMesh 以外のすべてに適用されます。

gizmoUseRelativeScreenSpace

これは gizmoUseScreenSpace に似ていますが、座標は 0.0 から 1.0 までの値として指定され、各ビューポートでビューポートの幅または高さのパーセントとして解釈され、addGizmoMesh 以外のすべてに適用されます。

addGizmoMarker markerType position flags unselColor selColor

これによって、マーカーからギズモが作成されます。「markerType」パラメータの値は次のとおりです。

#point #hollowBox #plusSign #asterisk #xMarker #bigBox #circle #triangle #diamond #smallHollowBox #smallCircle #smallTriangle #smallDiamond #dot #smallDot

position は、3D 空間上のポイントまたは 2D の画面空間上のポイントです。フラグは、addGizmoShape でサポートされるものと同じです。

addGizmoText text position flags unselColor selColor

これによって、画面上にテキストのギズモが作成されます。テキストはヒットテストを行ったり、マウスで選択したりすることはできません。これは表示のためだけに使用されます。

getLocalViewRay m

この関数はマウスの位置をとり、ビューの方向でそのマウス位置を通るレイを返します。戻り値は、マニピュレータ ターゲットを所有するノードのローカル座標で返されます。

「manip」 パッケージ

「manip」は、マニピュレータ用の有用なインタフェース関数のセットを含む、MAXScript の値です。manip がエクスポートする関数は次のとおりです。

manip.makeSphere position radius segments

これは、指定した位置、半径、セグメントを持つメッシュ球を返します。

manip.makeTorus position radius1 radius2 segments sides

指定された値を保持する円環体を作成します。

manip.makeBox position length width height lengthSegs widthSegs heightSegs

与えられたパラメータを使用してボックス メッシュを作成します。

manip.makePlaneFromNormal normal point

特定のポイントを通る指定された法線を持つ、平面オブジェクトを作成します。

manip.makeCircle center radius segments

与えられたパラメータを使用して、XY 平面に円シェイプを作成します。

manip.makeGizmoShape()

空のギズモ シェイプを作成します。使用方法については、後述の「GizmoShape」を参照してください。

平面オブジェクト

「manip.makePlaneFromNormal」から返される平面オブジェクトには、利用可能な次の関数があります。

intersectionPoint= [0,0,0]
plane.intersect ray &intersectionPoint

これは、与えられたレイを平面と交差させます。成功した場合はture、失敗した場合は false を返します。成功した場合、交差点は「intersectionPoint」値で設定されます。

plane.mostOrthogonal ray otherPlane

これは、与えられたレイに最も直交する平面を返します。つまり、ビュー方向に対して最も「四角い」平面です。

マニピュレータが、レイの投影に、2 つの別々の平面の間を選択することもあります。通常は、直接的にビュー レイに面する平面に投影するのが最善で、この関数がそれを決定します。使用例は、「UVWManip.ms」を参照してください。

plane.getNormal()

平面の法線を返します。

plane.getPoint()

平面が通るポイントを返します。

plane.getPlaneConstant()

平面定数の値を返します。これは、平面を定義する方程式の中の「D」の値です。

Ax + By + Cz + D = 0

GizmoShape オブジェクト

GizmoShape は、ワイヤフレーム ギズモの作成時に使用可能なシェイプ オブジェクトです。空の GizmoShape は次のように取得できます。

local giz = manip.makeGizmoShape()

これらの関数は、次のとおりです。

giz.addPoint point

シェイプに新しいポイントを追加します。このポイントは、ワイヤフレームを作るためにライン セグメントに接続されます。閉じたシェイプにする場合、最初のポイントを最後のポイントとして、再度追加する必要があります。

giz.startNewLine()

シェイプ内で新しいライン セグメントを開始します。

giz.transform matrix

指定した Matrix3 変換を使ってギズモを変換します。

注:GizmoShape を使用した 3D ギズモの作成例は、 UVWManip.ms を参照してください。

関連事項