オペレータは、必ずしも現在の PlayControl 位置で評価されているとは限りません。 代わりに、コンテキスト オブジェクトの時間値を使用します(OperatorContext.Time または OperatorContext::GetTime を参照)。 これは、各フレームのシミュレーション状態でキャッシュが可能なシミュレーション スタイル オペレータの場合に便利です。
ここで、オペレータの実装を最適化するためのヒントをいくつか紹介します。
OperatorContext.GetParameterValue または OperatorContext::GetParameterValue を使用して、オペレータのパラメータを読み取ります。 カスタム オペレータから読み込むより、迅速で便利です。
OperatorContext.GetInputValue または OperatorContext::GetInputValue を使用して、オペレータに接続するデータに簡単にアクセスできます。 入力がパラメータの場合、これによって実際のパラメータ値が返されます(数値など)。 入力がオブジェクトの場合(Primitive または Primitive、KinematicState または KinematicState、ClusterProperty または ClusterProperty など)、 そのオブジェクトを示す SDK オブジェクトが返されます。
キャッシュ済みデータを使用して、重複する計算を回避します。 キャッシュはお使いのプラグインのグローバル変数として格納できます。または、オペレータのインスタンスごとに別々のキャッシュが必要な場合は、OperatorContext または OperatorContext オブジェクトでユーザ データ メソッドを使用してください。 たとえば、スクリプト オペレータの速度は、math オブジェクト(SIMatrix3 または CMatrix3 など)を再利用することで、XSIMath.CreateMatrix3 または CMatrix3()を Update を呼び出すたびに作成し直すよりも改善されることがあります。
既存のレガシーまたはランタイム オペレータがある場合は、それらを自己インストール カスタム オペレータ(SICO)に移行することを強くお勧めします。 他のタイプのカスタム オペレータの固定インスタンスは、移行による影響を受けません。 オペレータ コードを SICO に移行すると、既存のオペレータを削除および再作成して、新しい SICO バージョンを使用しなければなりません。
ここで説明するオペレータの移行の種類には、Runtime to SICO と SPDL/Preset to SICO が含まれます。
オペレータにスクリプト コードを埋め込むのではなく、自己インストール プラグインをセットアップしてオペレータを定義した方が効率的です。 以下に、基本的な手順を示します。
オペレータを作成するコードが自己インストール プラグインにない場合、Update コールバックや他のコードを新しい自己インストール プラグインに設定し、コールを PluginRegistrar.RegisterOperator または PluginRegistrar::RegisterOperator に追加します。
同じプラグインにオペレータを作成するコマンドとオペレータ自体を含めることができますが、これらを個別のプラグインに分割することもできます。コマンドは実際には必須ではありません。
出力および入力ポート引数を 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 ; }
新しい自己インストール カスタム オペレータ API を使用するようにレガシー(プリセットベース)オペレータを変更するには、基本的に Custom Properties API を使用するように SPDL ベースのパラメータを変換する必要があります。また、新しいシグネチャと一致するよう既存のエントリ ポイントを更新し、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 オブジェクトにアクセスできます。そのため、オペレータのポートとカスタム パラメータに簡単にアクセスできます。
また、CustomOperator または CustomOperator には OperatorContext または OperatorContext のソースを使って(Context.Source または Context::GetSource を介して)アクセスすることもできます。ただし、OperatorContext または OperatorContext オブジェクトの方が、データで取得するには高速で便利です。たとえば、OperatorContext.GetInputValue または OperatorContext::GetInputValue は、特定の入力ポートに接続されているデータを返します。また OperatorContext.OutputPort または OperatorContext::GetOutputPort および OperatorContext.OutputTarget または OperatorContext::GetOutputTarget は、オペレータの出力ポートに書き込むことができます。
レガシー シーン ファイルが依存し続けない限り、オペレータの SPDL バージョンに関連付けられているプリセット、SPDL、および DLL を削除します。 両方のバージョンが共存しても、問題はありません。