一般的に、Softimage でスクリプトを記述するには、Softimage ネイティブ スクリプト コマンドと Softimage オブジェクト モデルという 2 つの方法を使用できます。
スクリプト コマンドは、一般的にユーザ インタフェース内のほとんどのユーザ アクションに対応する Softimage 固有の関数のセットです。たとえば、3D オブジェクトのシー グラフへの追加(CreatePrim)、シェーダの接続ポイントへの接続(SIConnectShaderToCnxPoint)、任意のプロパティ セット内のパラメータ値の変更(SetValue)などがあります。
オブジェクト モデルを使用すると、3D ワールドを模倣することによって、さらに深いレベルまでアクセスできます。3D ワールドでは、オブジェクト(Camera、NurbsSurfaceMesh、Material、Operator、ControlPoint、Parameter、Pass など)に対応するクラスのインスタンスを作成できます。これらのクラスには、そのオブジェクトに関する何かにアクセスできる関数(メソッドとプロパティ)が用意されています。たとえば、球の回転、頂点の色、カメラの視野、オペレータの接続ポイントなどです。
スクリプト コマンドとオブジェクト モデルの相違点の詳細については「Softimage SDK アーキテクチャについて」を参照してください。
オブジェクト モデルで記述されたスクリプトの方が、コマンドで記述されたスクリプトより高速に実行できるのには、次の 2 つの理由があります。
コマンドを実行すると、デフォルトで Script Editor にコマンド エコーがロギングされます。 コマンドのロギングには時間がかかるため、ログ ウィンドウが開くとスクリプトの実行が遅くなり、さらに開く回数が増えるほどスクリプトの実行が遅くなります。
オブジェクト モデルは、ロー レベルで Softimage のコア エンジンにアクセスします。つまり、コマンドでは、タスクを実行するために折衝しなければならないコードのレイヤがオブジェクト モデルより多くなります。 オブジェクト モデルは、基本的に Softimage のコア エンジンと直接折衝する COM(ActiveX)レイヤを使用して実装されています。
SetValue および GetValue から Parameter オブジェクトに移行する
基本的に、GetValue および SetValue コマンドは、Softimage シーン オブジェクトに関する多くの情報を含むプロパティ セット上で動作します。スクリプトコマンドからオブジェクトモデルに切り替えたい場合は、コマンドで実行していた処理をオブジェクトモデルに移行する方法を習得する必要があります。
GetValue コマンドと SetValue コマンドは、オブジェクト モデルの Parameter オブジェクトと ParameterCollection オブジェクトに相当します。これらのオブジェクトでほとんどすべての同じ関数が実行できます。たとえば、以下のJScriptコード例でSetValueを使用する方法を示します。
/* The following example uses SetValue to set various parameters on some scene elements */ NewScene( null, false ); // Create a sphere and change the wireframe color to red // This demonstrates we can use SetValue for object properties. CreatePrim( "Sphere", "MeshSurface" ); DeselectAll(); MakeLocal( "sphere.display", siNodePropagation ); SetValue( "sphere.display.wirecol", 15 ); CreatePrim( "Circle", "NurbsCurve" ); CreatePrim( "Circle", "NurbsCurve" ); CreatePrim( "Circle", "NurbsCurve" ); CreatePrim( "Circle", "NurbsCurve" ); // Set the radius of all circles to 1 // This demonstrates we can use SetValue in a bulk-edit fashion. SetValue( "circle*.circle.radius", 1 ); // Set the radius of "circle" to 2, and the radius of "circle1" to 3 // This demonstrates we can use SetValue with an array of values. SetValue( "circle.circle.radius,circle1.circle.radius", new Array(2,3) ); // Hide the grid in all views, for all cameras (except scene camera) // This demonstrates we can use SetValue for cameras, views, etc. SetValue( "Views.*.*.camvis.gridvis", false ); // Change current frame to 30 // This demonstrates we can use SetValue for playback settings SetValue( "PlayControl.Current", 30.000 );
以下に、同じ処理をほぼ完全にオブジェクト モデルで記述したスクリプトを示します(MakeLocal コマンドは除く)。
/* The following example uses SetValue to set various parameters on some scene elements */ NewScene( null, false ); var root = ActiveSceneRoot; // Create a sphere and change the wireframe color to red // This demonstrates the OM equivalent of using // SetValue for object properties. var obj = root.AddGeometry( "Sphere", "MeshSurface" ); var prop = MakeLocal( obj.Properties( "Display" ), siNodePropagation ); // MakeLocal returns an XSICollection containing the new local Display // property, so we can use it to get at the Wireframe Color parameter prop(0).Parameters( "wirecol" ).Value = 15; // Make four circles and add them to an XSICollection var circles = XSIFactory.CreateActiveXObject( "XSI.Collection" ); for (var i=0; i<4; i++) { circles.Add( root.AddGeometry( "Circle", "NurbsCurve" ) ); } // Set the radius of all circles to 1 // This demonstrates how to access the Radius parameter the long way var c = new Enumerator( circles ); for (; !c.atEnd(); c.moveNext() ) { c.item().Parameters( "radius" ).Value = 1; } // Set the radius of the 1st circle to 2, and the radius of the 2nd to 3 // This demonstrates how to access the Radius parameter with a shortcut circles(0).radius.Value = 2; circles(1).radius.Value = 3; // Since viewports are not available in the OM, there is no way to hide // the grid in all views, for all cameras (except scene camera) as you // can with SetValue: // // SetValue( "Views.*.*.camvis.gridvis", false ); // // However, you can hide the grid for the scene camera via the OM. // NB: The following three lines can all be combined into a single // line, but it has been split up here for readability: var scncam = root.FindChild( "", siCameraPrimType ); var camvis = scncam.Properties( "Camera Visibility" ) camvis.Parameters( "gridvis" ).Value = false; // Change current frame to 30 // This demonstrates the OM equivalent of using SetValue for playback // settings. // NB: Again, this could be a single line but is split for readability: var playctrl = ActiveProject.Properties( "Play Control" ) playctrl.Parameters( "Current" ).Value = 30.000;
ケース スタディ: アニメーション パスに沿って null を配置する
Michael Isner(www.isner.com)の好意により転載
通常の作業方法では、コマンドを使用しておおまかなスクリプトを作成してから、部分的にオブジェクト モデルを使用して改造し、高速化を図ります。 以下に、このようなプロセスの簡単な例を示します。この例では、球をアニメートし、すべてのフレームに null を配置します。
それでは、球をアニメートするスクリプトを使用して開始します。 以下は、ログ ウィンドウをカット アンド ペーストしたものです(読み易いように表面的な手直しを加えています)。
NewScene CreatePrim "Sphere", "NurbsSurface" SaveKey "sphere.kine.local.posx,sphere.kine.local.posy" _ & ",sphere.kine.local.posz", 1 SetValue "PlayControl.Current", 50 Translate , -16.6915152140782, 1.0970178921512E-15, -17.9162534756914, _ siRelative, siView, siObj, siXYZ SaveKey "sphere.kine.local.posx,sphere.kine.local.posy" _ & ",sphere.kine.local.posz", 50 SetValue "PlayControl.Current", 100 Translate , -17.3869950146648, -9.15956686650513E-16, 14.9592019311598, _ siRelative, siView, siObj, siXYZ SaveKey "sphere.kine.local.posx,sphere.kine.local.posy" _ & ",sphere.kine.local.posz", 100
すべてのフレームに null を配置するには、時系列で処理を繰り返す方法を知っておく必要があります。 タイムラインをスクラブするとフィードバックが得られます。
SetValue "PlayControl.Current", 27
したがって、時系列で処理を繰り返すには、以下のように記述します。
for i = 0 to 100 SetValue "PlayControl.Current", i next
続いて、フレームごとに null を取得する必要があります。この処理は簡単で、null の取得をカット アンド ペーストするだけです。
GetPrim "Null"
もちろん、GetPrim "Null" を実行するだけでは、100 個の null すべてを追跡するのは困難です。 最後に作成した null を取得するには、新しく作成した null(またはオブジェクト)を currentNull のような変数名に戻す必要があります。 この処理は以下のように記述します。
Set currentNull = GetPrim ("Null")
続いて、各フレームでの球の位置を取得し、そこに現在の null を配置する必要があります。 値を取得するには、その値をスライダ内で探して移動し、そのログをコピー アンド ペーストし、SetValue の箇所を GetValue に変更します。 したがって、球のグローバル位置を取得するには、[Global Transform]プロパティ エディタを開き、x、y、z の位置をスライドします。以下のコードが戻ります。
SetValue "sphere.kine.global.posx", -9.535 SetValue "sphere.kine.global.posy", 0.233 SetValue "sphere.kine.global.posz", -1.744
x = GetValue("sphere.kine.global.posx")
丸括弧を追加する必要もあることが確認できます。 変数と等しい結果を設定する場合はいつでも、コマンドでこの処理を実行する必要があります。 null を取得した場合とは異なり、冒頭に set を使用しなかったことに注意してください。 これは、x が単なる数値であるのに対して、currentNull はオブジェクトを戻すため、set キーワードを使用しなければならないからです。
それでは、すべてをまとめて、各フレームでの球の位置をロギングします。 このコードは、以下のようなります。
for i = 0 to 100 SetValue "PlayControl.Current", i x = GetValue("sphere.kine.global.posx") y = GetValue("sphere.kine.global.posy") z = GetValue("sphere.kine.global.posz") logmessage "("& x &","& y &","& z & ")" next
実に簡単ですね。それでは、その null を x、y、z の各値に移動します。 何かを移動すると、以下のコードが戻ります。
Translate , 0, 0, 0, siRelative, siView, siObj, siZ
相対的な座標(球からの戻り値で取得しているようなグローバルではない)を意味する siRelative が表示されているため、厳密には正しくないことがすぐに分かるはずです。 カット アンド ペーストしただけのコマンドで何が発生するか不明な場合、私はすぐにヘルプを参照することにしています。 このケースでは、siDeltaMode のモードとして、代わりに siAbsolute を使用する必要があることが分かりました。 したがって、null を取得する行も追加すると、以下のスクリプトになります。
dim i, x, y, z for i = 0 to 100 SetValue "PlayControl.Current", i x = GetValue("sphere.kine.global.posx") y = GetValue("sphere.kine.global.posy") z = GetValue("sphere.kine.global.posz") Set currentNull = GetPrim ("Null") Translate currentNull, x, y, z, siAbsolute, siView, siObj, siXYZ next
うまく動作しました。 冒頭に dim i, x, y, z の行を追加したことにお気づきになったかもしれません。 この行を追加したのは、変数を宣言するとクリプトの実行が高速になるためです。 すべてのスクリプト上に Option Explicit 行を配置する習慣をつけることをお勧めします。宣言していない変数が処理されるたびにエラーが発生するため、スクリプトのデバッグ(変数の入力ミスの指摘など)が速く簡単にできるからです。
スクリプトの実行速度が特に問題にならない場合もあります。 そのような場合は、おそらく広範なループがない簡単な処理を実行しているため、コマンドを使用するだけですみます。
ただし、高速化する必要がある処理を実行したいが、C++ で書き直す時間がないような場合、オブジェクト モデルは最適なオプションです。
以下に、処理にかかった秒数を戻すスクリプトの簡単なバリエーションを示します。
time_in = timer dim i, x, y, z for i = 0 to 100 SetValue "PlayControl.Current", i x = GetValue("sphere.kine.global.posx") y = GetValue("sphere.kine.global.posy") z = GetValue("sphere.kine.global.posz") Set currentNull = GetPrim ("Null") Translate currentNull, x, y, z, siAbsolute, siView, siObj, siXYZ next time_out = timer logmessage "elapsed time: " & round(time_out - time_in,3)
私のマシン上では、17.3611 秒かかりました。 この処理ではログ ウィンドウが開かれたことに注意してください。 時間計測スクリプトを実験する場合は、以下の点を了解しておいてください。
それでは、このスクリプトをオブジェクト モデルで修正した場合を見てみましょう。 最初に、位置の取得を見てみます。 以下に、オブジェクト モデルを使用した例を示します。
x = mySphere.Kinematics.Global.parameters("posx").value
GetValue などのコマンドを呼び出すのではなく、その球オブジェクトのプロパティを調べているのが確認できます。 これで数値が戻されるため、元のスクリプト内の GetValue をこのような行に置き換えることができます。
しかし、このスクリプトの本当の時間のロスは、時間をスクラブしていることです。 実際に、この球がはるかに大きなシーン内に配置された場合、PlayControl を修正すると、各フレームでのシーン内にあるすべてのオブジェクトの更新で非常に多くの時間をロスすることになります。 ここで実行できる大きな時間節約は、球の変換全体(Scale、Rotation、Position の記述)をピックアップすることです。 変換全体をピックアップすることで、値を読み取るタイミングを定義できます。 現在の時間で変換を読み取る処理は、以下のようになります。
set currentTransform = mySphere.Kinematics.Global.Transform
時間の経過とともに変換を取得する処理は、以下のようになります。
set currentTransform = mySphere.Kinematics.Global.Transform(currentFrame)
しかし、x = 34.333のような数値を簡単に扱う方法は利用できません。利用できるのは変換オブジェクトです。 『コマンドとスクリプト リファレンス』には、空間を操作するための役立つオブジェクト(SIMatrix3、SIMatrix4、SIQuaternion、SIRotation、SITransformation、SIVector3 など)が多数記載されています。また、もう少しスクロール ダウンして XSIMath を見ると、これらのオブジェクトを作成するツールや異なる空間の間で変換するツールが数多く見つかります。
SITransformation を含むこれらのオブジェクトのほとんどは、他のオブジェクトに変換するメソッドを含む多くのツールを提供します。ここでは、変換をxyz位置に変更します。少し調べると、xyz 位置のベクトルを戻す SITransformation.GetTranslation と呼ばれるメソッドがあることがわかります。したがって、スクリプトは以下のようになります。
set posVector = xsimath.CreateVector3 set currentTransform = mySphere.Kinematics.Global.Transform(i) currentTransform.GetTranslation posVector
これで位置を取得できたので、このコードを元のスクリプトに統合します。 SIVector3 のヘルプをのぞいてみるだけで、SIVector3 から x を取得する最も簡単な方法は、posVector.xを記述することだとわかります。 したがって、スクリプトは以下のようになります。
for i = 0 to 100 set posVector = xsimath.CreateVector3 set currentTransform = mySphere.Kinematics.Global.Transform(i) currentTransform.GetTranslation posVector Set currentNull = GetPrim ("Null") Translate currentNull, posVector.x, posVector.y, posVector.z, _ siAbsolute, siView, siObj, siXYZ next
しかし、このスクリプトを実行しても、うまく動作しません。 理由は、mySphere が何であるかをまだ定義していないためです。 コマンドでは、名前を持つだけで十分な場合がほとんどです。 しかし、オブジェクト モデルでは、常にオブジェクトが必要です。 この処理の最も簡単でよく使用する実行方法は、セレクションからです。 冒頭に以下の行を追加できます。
SelectObj "sphere" set mySphere = Selection(0)
プログラミングの経験がある場合、この操作方法があまり適切ではないと感じ、Selection を使わない方法を選択するかもしれません。 同じ処理を実行するもう 1 つの手法は、シーン ルートを取得し、それ以降で sphere という名前を検索する方法です。 以下に、これらの行を含むスクリプトを示します。
set oRoot = Application.ActiveProject.ActiveScene.Root set mySphere = oRoot.FindChild("sphere") for i = 0 to 100 set posVector = xsimath.CreateVector3 set currentTransform = mySphere.Kinematics.Global.Transform(i) currentTransform.GetTranslation posVector Set currentNull = GetPrim ("Null") Translate currentNull, posVector.x, posVector.y, posVector.z, _ siAbsolute, siView, siObj, siXYZ next
この時点で終わりにして、先に進むことができます。 オブジェクトモデルですべての処理を実行する必要はありません。 通常、私がオブジェクト モデルを使用するのは、パフォーマンスが格段に向上すると思われる場合や当面のタスクに対して最も便利な場合だけです。 しかし、ユーザにオブジェクト モデルを理解してもらうために、このスクリプトの分解を続けます。
スクリプトをちょっと見てみると、コマンドで実行している 2 つの処理(null の取得と変換)があることを確認できます。 変換について言えば、currentTransform を xyz 座標に変換するのではなく、変換を currentNull に入れ替えることで、多くの時間を節約できます。 スクリプトは以下のようになります。
set oRoot = Application.ActiveProject.ActiveScene.Root set mySphere = oRoot.FindChild("sphere") for i = 0 to 100 set currentTransform = mySphere.Kinematics.Global.Transform(i) Set currentNull = GetPrim ("Null") currentNull.Kinematics.Global.Transform = currentTransform next
ジョブを完了して、完全なオブジェクト モデルにするには、オブジェクト モデルから null を取得します。 AddPrimitive (X3DObject) メソッドの利点は、新しいオブジェクトを作成する際に親を定義する点です。したがって、球の子として null を配置すると、最終的なスクリプトは以下のようになります。
dim oRoot, mySphere, currentTransform, currentNull set oRoot = Application.ActiveProject.ActiveScene.Root set mySphere = oRoot.FindChild("sphere") for i = 0 to 100 set currentTransform = mySphere.Kinematics.Global.Transform(i) set currentNull= mySphere.AddPrimitive("Null") currentNull.Kinematics.Global.Transform = currentTransform next
この実行時間を計測したところ、私のマシン上では 2.3148 秒かかりました。 つまり、ボタンから実行するコマンド バージョンより 2.5 倍、UI から実行するコマンドより 7.5 倍速くなりました。