ヒントとテクニック

 
 
 

評価時間

オペレータは、必ずしも現在の PlayControl 位置で評価されているとは限りません。 代わりに、コンテキスト オブジェクトの時間値を使用します(OperatorContext.Time または OperatorContext::GetTime を参照)。 これは、各フレームのシミュレーション状態でキャッシュが可能なシミュレーション スタイル オペレータの場合に便利です。

最適化

ここで、オペレータの実装を最適化するためのヒントをいくつか紹介します。

  • 文字列よりインデックスでポートおよびグループを参照した方が効率的です。

  • OperatorContext.GetParameterValue または OperatorContext::GetParameterValue を使用して、オペレータのパラメータを読み取ります。 カスタム オペレータから読み込むより、迅速で便利です。

  • OperatorContext.GetInputValue または OperatorContext::GetInputValue を使用して、オペレータに接続するデータに簡単にアクセスできます。 入力がパラメータの場合、これによって実際のパラメータ値が返されます(数値など)。 入力がオブジェクトの場合(Primitive または PrimitiveKinematicState または KinematicStateClusterProperty または ClusterProperty など)、 そのオブジェクトを示す SDK オブジェクトが返されます。

  • キャッシュ済みデータを使用して、重複する計算を回避します。 キャッシュはお使いのプラグインのグローバル変数として格納できます。または、オペレータのインスタンスごとに別々のキャッシュが必要な場合は、OperatorContext または OperatorContext オブジェクトでユーザ データ メソッドを使用してください。 たとえば、スクリプト オペレータの速度は、math オブジェクト(SIMatrix3 または CMatrix3 など)を再利用することで、XSIMath.CreateMatrix3 または CMatrix3()Update を呼び出すたびに作成し直すよりも改善されることがあります。

オペレータの移行

既存のレガシーまたはランタイム オペレータがある場合は、それらを自己インストール カスタム オペレータ(SICO)に移行することを強くお勧めします。 他のタイプのカスタム オペレータの固定インスタンスは、移行による影響を受けません。 オペレータ コードを SICO に移行すると、既存のオペレータを削除および再作成して、新しい SICO バージョンを使用しなければなりません。

ここで説明するオペレータの移行の種類には、Runtime to SICOSPDL/Preset to SICO が含まれます。

SICO のランタイム

オペレータにスクリプト コードを埋め込むのではなく、自己インストール プラグインをセットアップしてオペレータを定義した方が効率的です。 以下に、基本的な手順を示します。

  1. オペレータを作成するコードが自己インストール プラグインにない場合、Update コールバックや他のコードを新しい自己インストール プラグインに設定し、コールを PluginRegistrar.RegisterOperator または PluginRegistrar::RegisterOperator に追加します。

    同じプラグインにオペレータを作成するコマンドとオペレータ自体を含めることができますが、これらを個別のプラグインに分割することもできます。コマンドは実際には必須ではありません。

  2. ランタイム オペレータに挿入されるグローバル変数について再検討します。

    • これらの変数がインスタンスごとのキャッシュである場合は、コンテキスト オブジェクトのユーザ データに埋め込む必要があります。 Init コールバックにユーザ データを格納し、それを Term コールバックで解放できます。

    • これらの変数がオペレータのすべてのインスタンスでグローバルである場合は、プラグインのグローバル変数になります。

  3. 出力および入力ポート引数を Update メソッドのシグネチャから削除し、Update コールバックの本文を書き直して、OperatorContext または OperatorContext オブジェクトからこれらを取得します。 入力ポートと出力ポートの実際の値を取得する方法はわずかに異なりますが、これらの構文的な問題が解決されたら、オペレータの実際のアルゴリズムは引き続き機能するはずです。

例: JScript ランタイムから C++ SICO への変換

以下に、ランタイム オペレータのオリジナルの JScript バージョンを示します。 太字の線は、Update 関数がポート接続を取得および設定する場所を示しています。

// ...Creation Code...
	var code = GeometryDeform_Update.toString();
	g_sop = XSIFactory.CreateScriptedOp( "GeometryDeform", code, "JScript" );

	var p1 = g_sop.AddIOPort( g_prim, "", 0, -1, siBranchGroupPort  );			
	var p2 = g_sop.AddInputPort( g_obj.posy, "", 0, -1, siBranchGroupPort  );			

	g_sop.Connect(g_obj);

// Notice that the port connections are passed in as arguments to Update
function GeometryDeform_Update( ctx, Out, Inpolymsh, Inposy )
{
	Application.LogMessage( "GeometryDeform_Update: " + Out.TargetPath + ":" + Inposy.TargetPath );
	
	var arr = Inpolymsh.Value.Geometry.Points.PositionArray.toArray();

	for ( var i=0; i<arr.length; i=i+3 )
	{
	  var x = arr[0+i];
	  var z = arr[2+i];
	  var y = 0;
 
	  var x0 = x;
	  var z0 = z + 1;
	  var l = Math.sqrt(x0 * x0 + z0 * z0)
	  var dif = l - Inposy.Value // In2 can be any value, e.g. posx
	  dif = dif * dif;
	  dif = Math.sqrt(dif);
	 
	  if ( dif < 1 ) {
		y = y + (1 - dif);
	  }
 
	  arr[1+i] = y;
	}
	Out.Value.Geometry.Points.PositionArray = arr;
}

以下は、変換後のオペレータです。 ここでも、太字の線は、Update 関数が SICO のポート接続を取得および設定する方法の違いを示しています。

//... from Creation Code (where prim is primitive of a sphere mesh)...
	var op = XSIFactory.CreateObject( "CppGeometryDeform" ) ;

	op.AddIOPort( prim, "", 0, -1, siBranchGroupPort ) ;
	op.AddInputPort( obj.posy, "", 0, -1, siBranchGroupPort ) ;

	op.Connect( ) ;
...

// Notice that only the context object is an argument to Update
function CppGeometryDeform_Update( in_ctxt )
{

	// The OperatorContext gives you access to the input ports
	var prim = ctxt.GetInputValue( 0 /* input primitive of IO port*/ ) ;
	var posyVal = ctxt.GetInputValue( 1 /* posy param */) ;

	var inGeom = prim.Geometry ;
	var arr = inGeom.Points.PositionArray.toArray() ;

	for ( var i=0; i<arr.length; i=i+3 )

	{
	  var x = arr[0+i];
	  var z = arr[2+i];
	  var y = 0;
 
	  var x0 = x;
	  var z0 = z + 1;
	  var l = Math.sqrt(x0 * x0 + z0 * z0)
	  var dif = l - Inposy.Value // In2 can be any value, e.g. posx
	  dif = dif * dif;
	  dif = Math.sqrt(dif);
	 
	  if ( dif < 1 ) {
		y = y + (1 - dif);
	  }
 
	  arr[1+i] = y;
	}

	// The OperatorContext gives you access to the output ports
	var outPrim = ctxt.OutputTarget ;
	outPrim.Geometry.Points.PositionArray( arr ) ;

	return CStatus::OK ;
}

別の手順を追加してオペレータをコンパイルする場合に備えて、上のコードと同等の C++ のコードも示します。

//... from Creation Code (where prim is primitive of a sphere mesh)...
	CustomOperator op = Application().GetFactory().CreateObject( L"CppGeometryDeform" ) ;

	op.AddIOPort( prim, L"", 0, -1, siBranchGroupPort ) ;
	op.AddInputPort( obj.GetParameters().GetItem( L"posy" ), L"", 0, -1, siBranchGroupPort ) ;

	op.Connect( ) ;
...

// Notice that only the context object is an argument to Update
XSIPLUGINCALLBACK CStatus CppGeometryDeform_Update( CRef& in_ctxt )
{

	OperatorContext ctxt( in_ctxt ) ;

	// The OperatorContext gives you access to the input ports
	Primitive prim = ctxt.GetInputValue( 0 /* input primitive of IO port*/ ) ;
	double posyVal = ctxt.GetInputValue( 1 /* posy param */) ;

	Geometry inGeom = prim.GetGeometry() ;
	CVector3Array posArray = inGeom.GetPoints().GetPositionArray();

	for ( LONG i=0 ; i<posArray.GetCount(); i++ )
	{
			CVector3& pos = posArray[i] ;
			pos.PutY( 0.0 ) ;

			CVector3 pos0( pos.GetX(), 0.0, pos.GetZ() + 1.0 )  ;
		
			double l = pos0.GetLength() ;

			double dif = l - posyVal ;
				
			if ( dif < 0 )
				dif = -dif ;

			if ( dif < 1 )
				pos.PutY( 1 - dif ) ;				
	}

	// The OperatorContext gives you access to the output ports
	Primitive outPrim = ctxt.GetOutputTarget() ;
	outPrim.GetGeometry().GetPoints().PutPositionArray( posArray ) ;

	return CStatus::OK ;
}

SPDL/プリセットからSICOへ

新しい自己インストール カスタム オペレータ API を使用するようにレガシー(プリセットベース)オペレータを変更するには、基本的に Custom Properties API を使用するように SPDL ベースのパラメータを変換する必要があります。また、新しいシグネチャと一致するよう既存のエントリ ポイントを更新し、PluginRegistrar または PluginRegistrar でオペレータを登録するようにいくつかの新しいコールバックを実装します。

ヒント:

変換プロセスを開始する前に、カスタム オペレータの実装方法について、明確に理解する必要があります。 オペレータの適用の詳細については、「Softimage でオペレータを適用する」を参照してください。コールバックおよびエントリ ポイントの使い方の詳細については、「オペレータのコールバック」を参照してください。

  1. C++ API を使用してオペレータを実装する場合は、プロジェクト ファイル(.vcproj.sln)を変更して dll を¥Application¥Plugins に出力します(¥Application¥bin¥ には出力しません)。

  2. XSILoadPlugin エントリ ポイント(オペレータを登録します)を追加します。

  3. SPDL 内のパラメータ定義を確認し、同等のパラメータを DefineLayout コールバックに作成します。

  4. SPDL のポート接続情報を使用して、オペレータを適用するためのコードの出力と入力のリストを作成します。

    ヒント:

    SPDL には解読が難しい GUID が含まれているため、SPDL ベースのオペレータを含むシーンをロードし、SDK Explorer を使用してそのシーンに接続する実際のオブジェクをインスペクトすると便利です。

  5. ただし、オペレータを適用する場合、ApplyOp の各呼び出しを確認し、手動または自動のアプリケーション コードで置換します。 以下のいずれかの操作を実行できます。

    • AddCustomOp を使用するメニュー コールバックを追加し、オペレータを作成します。詳細については、「Splatter の例」を参照してください。

    • カスタム コマンドを追加して、オペレータを適用し、ApplyOp への呼び出しを変更して新しいコマンドを使用します。 「頂点カラー ミキサの例」で使用されているオペレータと同じプラグインで実装できます。

  6. 新しいシグネチャ、新しいコンテキスト オブジェクト、および新しい API を使用するよう Update 関数を変更して、入力と出力を取得します(実際のアルゴリズムは変更しないでください)。

  7. オペレータをテストします。

  8. 必要に応じて、シーン ファイルに保存されているオペレータを新しいオペレータで置き換えます。

  9. レガシー シーン ファイルが依存し続けない限り、オペレータの SPDL バージョンに関連付けられているプリセット、SPDL、および DLL を削除します。 両方のバージョンが共存しても、問題はありません。