オペレータは、必ずしも現在の PlayControl 位置で評価されているとは限りません。 代わりに、コンテキスト オブジェクトの時間値を使用してください(「OperatorContext.Time」または「OperatorContext::GetTime」を参照)。これは、各フレームのシミュレーション状態でキャッシュが可能なシミュレーション スタイル オペレータの場合に便利です。
ここで、オペレータの実装を最適化するためのヒントをいくつか紹介します。
オペレータのパラメータを読み取る際は、OperatorContext.GetParameterValue または OperatorContext::GetParameterValue を使用します。カスタムオペレータから読み込むより、迅速で便利です。
OperatorContext.GetInputValue または OperatorContext::GetInputValue を使用すれば、オペレータに接続されているデータに簡単にアクセスできます。入力がパラメータの場合、これによって実際のパラメータ値が返されます(数値など)。入力がオブジェクト(Primitive または Primitive、KinematicState または KinematicState、ClusterProperty または ClusterProperty など)の場合は、そのオブジェクトを示す SDK オブジェクトが返されます。
キャッシュ済みデータを使用して、重複する計算を回避します。 キャッシュは、プラグインのグローバル変数として保存できます。また、オペレータの各インスタンスについて個別のキャッシュが必要な場合は、OperatorContext または OperatorContext オブジェクトでユーザ データ メソッドを使用してください。たとえば、Update の呼び出しごとに算術オブジェクトを作成し直す(XSIMath.CreateMatrix3 または CMatrix3())のではなく、算術オブジェクトを再利用すれば(SIMatrix3 や CMatrix3 など)、スクリプト オペレータの速度が向上する場合があります。
既存のレガシーまたはランタイム オペレータがある場合は、それらを自己インストール カスタム オペレータ(SICO)に移行することを強くお勧めします。 他のタイプのカスタム オペレータの固定インスタンスは、移行による影響を受けません。 オペレータ コードを SICO に移行すると、既存のオペレータを削除および再作成して、新しい SICO バージョンを使用しなければなりません。
ここで説明するオペレータの移行の種類には、SICO のランタイム と SPDL/プリセットから SICO へ が含まれます。
オペレータにスクリプト コードを埋め込むのではなく、自己インストール プラグインをセットアップしてオペレータを定義した方が効率的です。 以下に、基本的な手順を示します。
オペレータを作成するコードがまだ自己インストール プラグインにない場合は、Update コールバックと他のコードを直接新しい自己インストール プラグインに配置し、PluginRegistrar.RegisterOperator または PluginRegistrar::RegisterOperator の呼び出しを追加します。
同じプラグインにオペレータを作成するコマンドとオペレータ自体を含めることができますが、これらを個別のプラグインに分割することもできます。コマンドは実際には必須ではありません。
Update メソッド シグネチャからすべての出力ポート引数と入力ポート引数を削除し、Update コールバックのメイン部分を書き直して、OperatorContext または OperatorContext オブジェクトからこれらを抽出します。入力ポートと出力ポートの実際の値を取得する方法はわずかに異なりますが、これらの構文的な問題が解決されたら、オペレータの実際のアルゴリズムは引き続き機能するはずです。
以下に、ランタイム オペレータのオリジナルの 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 ; }
レガシー(プリセットベースの)オペレータを、新しい自己インストール カスタム オペレータ API を使用するように変更することは、基本的に、SPDL ベースのパラメータをカスタム プロパティ API を使用できるように変換し、既存のエントリ ポイントを更新して新しいシグネチャに一致させ、いくつかの新しいコールバックを実装して PluginRegistrar または PluginRegistrar でオペレータを登録することです。
変換プロセスを開始する前に、カスタムオペレータの実装方法について、明確に理解する必要があります。オペレータの適用の詳細については、「Softimage でオペレータを適用する」を参照してください。コールバックおよびエントリ ポイントの使い方の詳細については、「オペレータ コールバック」を参照してください。
C++ APIを使用してオペレータを実装する場合は、プロジェクトファイル(.vcproj、.sln)を変更してdllを¥Application¥Pluginsに出力します(¥Application¥bin¥には出力しません)。
オペレータを登録する XSILoadPlugin エントリ ポイントを追加します。
SPDL のパラメータ定義を確認し、DefineLayout コールバックに同等のパラメータを作成します。
SPDL のポート接続情報を使用して、オペレータを適用するためのコードの出力と入力のリストを作成します。
接続が基本的なもので、複数インスタンス グループやオプションのポートをまったく使用していない場合、オペレータを自動的に適用できます。
接続がオプションまたはダイナミックである場合は、オペレータを手動で適用する必要があります。
ただし、オペレータを適用することにした場合、ApplyOp の各呼び出しを検索し、それをマニュアルまたは自動のアプリケーション コードで置き換える必要があります。以下のいずれかの操作を実行できます。
AddCustomOp を使用するメニュー コールバックを追加して、Splatter の例の説明に従ってオペレータを作成します。
オペレータを適用するカスタム コマンドを追加し、ApplyOp のすべての呼び出しを変更して、新しいコマンドを使用します。「頂点カラー ミキサの例」の説明に従って、オペレータとして同じプラグインに実装できます。
新しいシグネチャ、新しいコンテキスト オブジェクト、および新しい 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 を使用すれば、オペレータの出力ポートに書き込むことができます。
レガシー シーン ファイルが依存し続けない限り、オペレータの SPDL バージョンに関連付けられているプリセット、SPDL、および DLL を削除します。 両方のバージョンが共存しても、問題はありません。