ペイント スクリプト ツール用の MEL スクリプトの記述
 
 
 
注:このセクションは、MEL スクリプトとプログラミングに関する知識があるユーザを対象に記述されています。MELの詳細については、『MEL とエクスプレッション』マニュアルを参照してください。

ペイント スクリプト ツール(Paint Scripts Tool)は、2D の数値配列を NURBS サーフェスやポリゴン サーフェス上に重ねて表示するという概念に基づいています。この数値配列は、サーフェス上にある頂点の位置、またはサーフェスのパラメータ空間に均等に配置された任意の 2D グリッドで定義されます。この数値配列は、各ピクセルが配列位置を表し、グレー スケール値がその配列位置の数値を表すような 2D のグレー スケール イメージと考えることができます。ペイント スクリプト ツール(Paint Scripts Tool)用の MEL スクリプトは、この数値配列をアーティザンによってどのように処理するかを指示するものです。上記で説明した配列位置の数値がアーティザンによって参照されるたびに、スクリプトがコールされます。また、配列位置の 1 つの数値がアーティザンによって変更される時にも、スクリプトがコールされます。

スクリプト ペイント MEL プロシージャ

ペイント スクリプト ツール(Paint Scripts Tool)は、複数の MEL プロシージャによって定義されます。個々の MEL プロシージャの名前は、ツール設定(Tool Settings)エディタのセットアップ(Setup)セクションに表示されます。MEL プロシージャは、MEL コマンド artUserPaintCtxを使用して設定することもできます。ここでは、次のアーティザンの MEL プロシージャについて説明します。

以降の説明では、コマンドの設定に使用する artUserPaintCtx フラグをカッコ()で囲んで記載しています。

ツール セットアップ コマンド(-tsc "ToolSetupCommand")

定義されている場合、ToolSetupCommand プロシージャは、ペイント スクリプト ツール(Paint Scripts Tool)が選択( 修正 > ペイント スクリプト ツール(Modify > Paint Scripts Tool)メニュー項目、またはツール ボックス(Tool Box)やシェルフから選択)された直後にコールされます。通常、このプロシージャは、他のすべてのアーティザン MEL プロシージャを設定するために使用されます。このプロシージャは、次のように定義します。

global proc ToolSetupCommand (
	string $toolContextName
)
{
	// This is an example artUserPaintCtx command that would setup
	// the following Artisan procedures. This is not a realistic
	// example because you would never have all the procedures
	// defined.
	//
	artUserPaintCtx -e
		-tcc "ToolCleanupCommand"
		-gac "GetArrayAttributeCommand"
		-ic "InitializeCommand" -fc "FinalizeCommand"
		-svc "SetValueCommand" -gvc "GetValueCommand"
		-gsc "GetSurfaceCommand"
		$toolContextName;
}

この場合、$toolContextName は選択されたツール コンテキストの名前です。artuserPaintCtx コマンドの最後の引数として使用されます。

ツール クリーンアップ コマンド(-tcc "ToolCleanupCommand")

定義されている場合、ペイント スクリプト ツール(Paint Scripts Tool)が終了する直前に、ToolCleanupCommand がコールされます。このプロシージャは、次のように定義します。

global proc ToolCleanupCommand (
	string $toolContextName
)
{
	...
}

この場合、$toolContextName は終了したツール コンテキストの名前です。

配列アトリビュート取得コマンド(-gac "GetArrayAttributeCommand")

このコマンドを定義すると、初期化コマンド、終了コマンド、値設定コマンド、値取得コマンド用に定義したコマンドは無視されます。

GetArrayAttributeCommand プロシージャが定義されている場合、ペイント操作を適用する NURBS サーフェスを選択する度にコールされます。このプロシージャは文字列を返します。この文字列は、特定のディペンデンシー ノードに関する二重配列アトリビュートを表す名前のリストです。このプロシージャは、次のように定義します。

global proc string GetArrayAttributeCommand (
	string $surfaceName
)
{
	string $arrayAttributes
	// determine array attributes that correspond to
	// $surfaceName
	...
	return $arrayAttributes;
}

この場合、$surfaceName は、返される配列アトリビュートが割り当てられる NURBS サーフェスの名前です。複数の配列アトリビュートが返されたときに、アーティザンで値が必要とされるが、すべての値がアーティザンにより記述されている場合、最初の値が読み込まれます。

初期化コマンド(-ic "InitializeCommand")

定義されている場合、InitializeCommand プロシージャは、各ストロークが開始される前に、ペイント可能な NURBS サーフェスごとにコールされます。このプロシージャは、次のように定義します。

global proc string InitializeCommand(
	int $surfaceName
)
{
	string $flags;
	// build up $flags
	//
	...
	return $flags;
}

この場合、$surfaceName は NURBS サーフェスの名前です。たとえば、次のディレクトリにある spherePaint.mel と geometryPaint.mel を参照してください。

このプロシージャは次の処理を実行する必要があります。

上記のすべての情報は、プロシージャから返される $flags 変数を通じてアーティザンに渡されます。この $flags 変数はフラグと引数を連結した文字列で、標準の MEL プロシージャに渡されるフラグおよび引数とほぼ同じものです。これらがペイント スクリプト ツール(Paint Scripts Tool)によって認識されるフラグです。$flags 変数の各要素では、短縮形式/完全形式のフラグ(-short/long)の後に引数を指定します。

終了コマンド(-fc "FinalizeCommand")

定義されている場合、FinalizeCommand プロシージャは、各ストロークが終了した後で、ペイント可能な NURBS サーフェスごとにコールされます。このプロシージャは、次のように定義します。

global proc FinalizeCommand(
	int $surfaceID
)
{
	...
}

この場合、$surfaceID は、NURBS サーフェスの InitializeCommand によって定義されたサーフェス ID です。

値設定コマンド(-svc "SetValueCommand")

定義されている場合、SetValueCommand プロシージャは、NURBS サーフェス上の特定の位置に値が割り当てられるときにコールされます。このプロシージャは、1 つの位置について一度コールされます。したがって、Artisan のペイント操作によってサーフェス上で 10 個の位置の値を変更すると、SetArrayAttributeCommand は 10 回コールされます。このプロシージャは、次のように定義します。

global proc SetValueCommand(
	int $surfaceID,
	int $index,
	float $value,
	//
	// The following arguments are only passed if requested by
	// InitializeCommand. However this is the order that they
	// will be passed in.
	//
	float $u,			// ($u,$v) is UV location on surface
	float $v,				
	float $px,			// ($px,$py,$pz) is position on surface
	float $py,
	float $pz,
	float $nx,			// ($nx,$ny,$nz) is surface normal
	float $ny,
	float $nz
)
{
	...
}

この場合、

このプロシージャに渡される引数は、NURBS サーフェスの InitializeCommand プロシージャの戻り値に応じて異なります。

値取得コマンド(-gvc "GetValueCommand")

定義されている場合、GetValueCommand プロシージャは、NURBS サーフェス上の特定の位置に割り当てられた値が要求されたときにコールされます。このプロシージャは、次のように定義します。

global proc float GetValueCommand(
	int $surfaceID,
	int $valueIndex
)
{
	float $value
	// determine the value associated with $valueIndex location
	// on surface $surfaceID
	//
	...
	return $value;
}

この場合、

サーフェス取得コマンド(-gsc "GetSurfaceCommand")

このプロシージャを定義することはほとんどありません。定義されている場合、アーティザンでセレクション リストが処理されると必ず、セレクション リストの依存ノードすべてについて、GetSurfaceCommand が 1 度ずつコールれます。通常、このような処理が発生するのは、ペイント スクリプト ツール(Paint Scripts Tool)を選択している時です。このプロシージャは、次のように定義します。

global proc string GetSurfaceCommand (
	string $selectedDependencyNode
)
{
	string $surface;
	// Set $surface based on $selectedDependencyNode.
	// Set $surface to NULL string (""), if no surface 
	// is to be painted because of $selectedDependencyNode.
	//
	...
	
	return $surface;
}

このプロシージャを定義すると、セレクション リストに対するアーティザンのデフォルト処理はオーバーライドされます。

スクリプト ペイントのスクリプトの例

このセクションでは、注釈付きのスクリプトを例に挙げ、全体的なスクリプト レイアウトについて説明します。

全体的なスクリプト レイアウト

ペイント スクリプト ツール(Paint Scripts Tool)で使用するスクリプトは、さまざまなレイアウトで記述することができます。このサンプル スクリプトのレイアウトは、次のディレクトリにあるアーティザン スクリプトの例で使用されているものです。

このレイアウトでは、MEL プロシージャがすべて 1 つのファイルに維持されるので、ツール設定(Tool Settings)エディタのセットアップ(Setup)セクションにあるツール セットアップ コマンド(Tool Setup Cmd)ボックスに値を入力するだけで、スクリプトがすべて定義できます。このプロシージャでは、artuserPaintCtx コマンドを使用して、他のすべての MEL プロシージャを設定しています。次に、spherePaint スクリプトの非常に大まかなレイアウトを示します。

// Define global variables used throughout the script
//
global ...;
// Define the Tool Setup Cmd procedure. This procedure will
// setup all the other MEL procedures used by the Paint //Scripts tool.
//
// "spherePaint" would be entered into the Tool Setup Cmd field in
// the Setup section of the tool settings window.
//
global proc spherePaint( string $context )
{
	artUserPaintCtx -e
		-ic "initSpherePaint"
		-fc "finishSpherePaint"
		-svc "setSpherePaintValue"
		-gvc "getSpherePaintValue"
		-gsc ""
		-gac ""
		-tcc ""
		$context;
}
// Define various procedures that were mentioned in the Tool
//Setup Cmd procedure above. All the procedures defined by
//the artUserPaintCtx command have to be global.
//
global proc string initSpherePaint( string $surfaceName )
{
	...
}
...

spherePaint.mel

これは、次の場所にある spherePaint.mel ファイルの注釈を加えたバージョンです。

このスクリプトは NURBS サーフェスでのみ有効です。

//
// This is a simple example script for the Artisan Paint 
//Scripts tool. It will paint spheres onto the selected
//surfaces. The size of the spheres are controlled by the
//painted values.
//
// Usage:
// 1) Place this script into your scripts directory (usually
//the maya/scripts directory in your home directory
// 2) Select the Paint Scripts Tool (Modify > Paint Scripts
// Tool) and bring up the Tool Settings window
// 3) Go to the Setup section and enter "spherePaint" into 
// the "Tool Setup Cmd" field and hit enter
// 4) Paint Geometry
//
// Tips:
// Once you have the Geometry Paint Tool setup you may want
// to it from the toolbar to the shelf so that it is always
// accessible
//

下記のグローバル変数は、ペイントされているサーフェスの数と、サーフェスの ID を把握するために使用されます。$sphereNamePrefix は文字配列であり、アクティブなサーフェスごとに 1 つの文字列が格納されます。この文字列型配列の特定のエントリが空の文字列("")である場合は、そのエントリの配列インデックスをサーフェス ID として使用できます。エントリが空の文字列でない場合は、球体を作成する時に、その文字列を球体名のプリフィックスとして使用します。$spherePaintFreeSlot は $sphereNamePrefix 配列内で空いている最初のエントリのインデックスで、$spherePaintSlots は $sphereNamePrefix の現在のサイズ(サーフェスの数)です。このように、サーフェス ID を配列のインデックスとして使用し、各サーフェスごとの情報を保存する手法は、アーティザンの他のサンプル スクリプトでも頻繁に利用されています。

// These are global variables used to keep track of multiple
// surfaces and the name prefixes used for the spheres on each
// surface
//
global string $sphereNamePrefix[];
global int $spherePaintFreeSlot = 0;
global int $spherePaintSlots = 0;

spherePaint はペイント スクリプト ツール(Paint Scripts Tool)の全体的な構造を設定するプロシージャで、さまざまな状況のもとでコールされる MEL プロシージャを指定しています。

// This procedure should be set as the "Tool Setup Cmd" in the 
// Setup section of the Maya Artisan Paint Scripts Tool’s tool settings
// window. The tool context is supplied as an argument.
//
global proc spherePaint( string $context )
{
	// initialize all the other commands in this scriptable 
	// paint tool context.
	// 
	artUserPaintCtx -e
		-ic "initSpherePaint"
		-fc "finishSpherePaint"
		-svc "setSpherePaintValue"
		-gvc "getSpherePaintValue"
		-gsc ""
		-cc ""
		-tcc ""
		-gac ""
		$context;
}
// This is the "Initialize Cmd". This procedure is called once
// for every selected surface when an initial click is received
// on any surface. The argument is the name of the surface. This
// procedure returns a string which indicates to the scriptable
// tool how to behave for the duration of the stroke. 
//
global proc string initSpherePaint( string $name )
{
	global string $sphereNamePrefix[];
	global int $spherePaintFreeSlot;
	global int $spherePaintSlots;
	int $slot;

最初に、サーフェスに割り当てるサーフェス ID を決めるために、$sphereNamePrefix 配列内で空いている最初のエントリを探します。空いたエントリが見つかると、そのエントリのインデックスをサーフェス ID($slot)として使用します。

	// find a free slot for this surface in the global arrays
	//
	for ( $slot = $spherePaintFreeSlot; $slot < $spherePaintSlots; $slot++ )
	{
		if ( $sphereNamePrefix[$slot] == "" ) {
			break;
		}
	} 
	if ( $slot == $spherePaintSlots ) {
		$spherePaintSlots++;
		$spherePaintFreeSlot = $spherePaintSlots;
	}

次に、$name 引数で渡された値が NURBS サーフェスと一致するかどうかを調べます。一致する場合は、ペイントされる球体用のプリフィックスを作成して、そのプリフィックスを $sphereNamePrefix 配列の適切なエントリに格納します。

	if ( ‘nodeType $name‘ == "nurbsSurface" ) {
		// save the name of the parent of this shape as well
		// as a prefix to use when creating the spheres
		//
		string $parent[] = `listRelatives -p $name`;
		$sphereNamePrefix[$slot] = $parent[0] + "Sphere";
	}

初期化プロシージャの最終段階では、サーフェスの処理方法を指示する文字列を作成して、アーティザンに返します。前に説明したように、この文字列は特殊なフラグと引数を連結したものです。この場合、アーティザンに対し、この文字列から次のフラグが渡されます。

	// Return an argument string which:
	// - tells the tool what surface ID to use for this surface
	// - indicates that values should be distributed on a 20x20
	// grid on the surface
	// - indicate that the associated world space position
	// should also be passed to the "Set Value Cmd".
	//
	return ( "-id " + $slot
 + " -grid 20 20"
 + " -position world");
}
// This is the "Finalize Cmd". This procedure is called at the
// end of the stroke. It is passed the surface ID, that was
// generated by the "Initialize Cmd".
//
global proc finishSpherePaint( int $slot )
{

このプロシージャは、ストロークの終了時にコールされます。ここでは $slot の値に基づき、$sphereNamePrefix 配列内で現在のサーフェスに対応するエントリをクリアしています。また、空いている最初のエントリのインデックスよりもサーフェス ID($slot)が小さい場合は、$spherePaintFreeSlot に $slot の値を代入します。

	
	global string $sphereNamePrefix[];
	global int $spherePaintFreeSlot;
	// clear out the slot that was used for this surface
	//
	$sphereNamePrefix[$slot] = "";
	if ( $slot < $spherePaintFreeSlot ) {
		$spherePaintFreeSlot = $slot;
	}
}
// This is the "Set Value Cmd". It is called everytime a value
// on the surface is changed. A surface ID, a grid index
// on the surface and the value associated with that grid index
// is passed. There can be additional arguments depending on the
// options generated by the return value of the "Initialize Cmd".
// In this case the (x,y,z) surface position for this grid point
// is also passed.
// 
global proc setSpherePaintValue(
	int $slot,
	int $index,
	float $val,
	float $x,
	float $y,
	float $z
)
{
	global string $sphereNamePrefix[];

$slot が有効なサーフェス ID であるかどうかを調べるために、$sphereNamePrefix 配列内の対応するエントリが空の文字列でないことを確認します。

	if ( $sphereNamePrefix[$slot] != "" ) {

ペイントされる球体のオブジェクトには一意の名前が割り当てられます。この名前は、サーフェスの $sphereNamePrefix とグリッドのインデックスを連結したものです。この一意の名前に基づいて、特定のグリッド位置に対して既にペイント操作が適用されたかどうか(球体が既に存在するかどうか)が判断されます。この方法が不利な点は、initSpherePaint で指定されたグリッド サイズに影響されやすいことです。たとえば、44 というインデックスは、サイズが 10×10 のグリッド上ではグリッド位置(4, 4)に対応します。ところが、グリッド サイズを 20×20 に変更すると、同じ44 というインデックスがグリッド位置(2, 4)に対応することになります。

		// determine the name of the sphere associated with this
		// grid location 
		//
		string $objname = $sphereNamePrefix[$slot] + $index;
		if ( ‘objExists $objname‘ ) {

球体が既に存在する場合は、引数で渡された $val の値に基づいて、球体が均等にスケールされます。特殊なケースとして $val の値が 0 以下である場合は、既存の球体が削除されます。

			// if the sphere already exists, use the value to
			// adjust the size of the sphere. If the value is
			// 0, the sphere is deleted
			//
			if ( $val > 0 ) {
				scale $val $val $val $objname;
			} else {
				delete $objname;
			}
		} else if ( $val > 0 ) {

一意の名前に対応するグリッド位置に球体が存在しない場合は、MEL コマンドである sphere を使用して球体を作成します。その際、sphere MEL コマンドの -name オプションを使用して、上記の一意の名前を新しい球体に割り当てます。球体が作成できたら、引数 $val で渡された値に基づいて、球体を均等にスケールします。また、原点(球体が作成されるデフォルト位置)から引数($x、$y、$z)で渡されたワールド座標位置に球体を移動し、球体が正しい位置に表示されるようにします。

			
			// the sphere doesn’t exist
			//
			string $sname[];
			// create a sphere with the proper name, scale it by
			// the passed value and parent the sphere to the same
			// parent as the surface we are painting on
			//
			$sname=‘sphere -ch off -name $objname‘;
			if ( $sname[0] != $objname ) {
				print ("SPHERE NAME FAILED: wanted "
					+ $objname + " got " + $sname[0] + "\n");
			}
			scale $val $val $val;
			move $x $y $z;
		}
	}
}
// This is the "Get Value Cmd". It is called everytime a value
// on the surface is needed by the scriptable paint tool. A
// surface ID and a grid index is passed in. This procedure should
// return the value for this grid location on the specified surface.
// 
global proc float getSpherePaintValue( int $slot, int $index )
{
	global string $sphereNamePrefix[];
	if ( $sphereNamePrefix[$slot] != "" ) {
		// if this slot is valid, generate the name for the
		// sphere at this grid index
		//
		string $objname = $sphereNamePrefix[$slot] + $index;

このプロシージャは、特定のグリッド位置の値が要求された時にコールされます。このスクリプトの値は、球体のスケール係数です。

		
		if ( ‘objExists $objname‘ ) {

$index で指定された位置に球体が既にペイントされている場合は、球体の X スケール係数が返されます。X スケール係数が返されることに特に意味はありません。Y スケール係数や Z スケール係数、またはすべてのスケール係数の平均値も簡単に返すことができます。プロシージャの戻り値はすべてスクリプトによって決まります。

			
			// if the sphere exists, return the X scale factor
			// as the value for this grid location
			//
			return ‘getAttr ($objname + ".sx")‘;
		} else {

指定された位置に球体が存在しない場合は、0.0 が返されます。

			// the sphere doesn’t exist, therefore return 0 as
			// the value for this grid location
			//
			return 0.0;
		}
	} else {
		return 0.0;
	}
}

このスクリプトの機能を理解するためのヒントとして、次の値を変更してみてください。

グリッド サイズを変更する

89 行目を次のように変更してください。

 + " -grid 20 20"

変更後:

 + " -grid 40 30"
		

球体の代わりにコーンをペイントする

154 行目を次のように変更してください。

			$sname=‘sphere -ch off -name $objname‘;

変更後:

			$sname=‘cone -ch off -name $objname‘;

グリッドにジッタを付加する

次の行を、

 + " -jitter true"

90 行目の前に追加してください。