ヒントとテクニック

 
 
 

評価時間

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

最適化

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

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

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

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

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

オペレータの移行

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

ここで説明するオペレータの移行の種類には、SICO のランタイムSPDL/プリセットから 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 を使用するように変更することは、基本的に、SPDL ベースのパラメータをカスタム プロパティ API を使用できるように変換し、既存のエントリ ポイントを更新して新しいシグネチャに一致させ、いくつかの新しいコールバックを実装して 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 関数を変更して、入力と出力を取得します(実際のアルゴリズムは変更できません)。

    • 古い Update コールバックでは、すべてのポート接続を引数として渡すように要求されました。 新しい Update コールバックでは、1 つの引数、つまりコンテキストのみがサポートされています。ここから、ポート接続とカスタム パラメータ(変数)を取得できます。

    • 古い Update コールバックでは、UpdateContext または UpdateContext オブジェクトにアクセスできました。新しい Update コールバックでは、OperatorContext または OperatorContext オブジェクトにアクセスできます。これにより、オペレータのポートおよびカスタム パラメータに簡単にアクセスできるようになりました。

    • OperatorContext または OperatorContext のソースを介して(Context.Source または Context::GetSource を介して)、今までどおり CustomOperator または CustomOperator にアクセスすることができます。ただし、OperatorContext または OperatorContext オブジェクトを使用すれば、より速く効率的にデータにアクセスできます。たとえば、OperatorContext.GetInputValue または OperatorContext::GetInputValue は、指定された入力ポートに接続されているデータを返します。OperatorContext.OutputPort または OperatorContext::GetOutputPort、および OperatorContext.OutputTarget または OperatorContext::GetOutputTarget を使用すれば、オペレータの出力ポートに書き込むことができます。

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

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

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