myara CG blog

CG Design Blog. Thoughts, experiments and experiences.

SI | Memo : GetGeometry2()

GetGeometry2

ポイントの位置を取得するために使ってみました。便利なのでメモっておきます。

このMethodを使うと、フレームとコンストラクションモードを指定が出来ます。
何がいいかというと、オブジェクトにモーションが付いていても、他のフレームや、元のジオメトリのポイント位置を取得することが出来ます。

例えば、X<0 のポイントを選択するファンクションを作ってみましょう:


SelectObj (HalfPoints(selection(0), 1) )

function HalfPoints(oObj, kine, zero_Range){
// X < 0 にすると 0 も含まれるので、このzero_Rangeの数値をひいておこう
// 指定されていなければ、0.0001にしましょう。
var zero_Range = zero_Range == null ? 0.0001 : zero_Range

//ポイントを取得
var oPoints = oObj.ActivePrimitive.Geometry.Points;

//ポイントの数を取得 (ポイントの数を取得しておいた方がループが速くなります)
var oPointsCount = oPoints.Count;

//ターゲットポイントのコレクションを作っておく
var oTargetPoints = oObj.ActivePrimitive.Geometry.CreateSubComponent(siVertexCluster);

//ポイントをループ
for ( var i=0; i < oPointsCount; i++ ) {
// ポイントの位置を取得
// kine = 0 = グローバル値
// kine = 1 = ローカル値
var oPos = kine == 1 ? oPoints(i).Position : XSIMath.MapObjectPositionToWorldSpace(oObj.Kinematics.Global.Transform, oPoints(i).Position);
if (oPos.x < 0 - zero_Range){
oTargetPoints.Addelement(oPoints(i).index)
}
}
return oTargetPoints;
}


すると、こうなります:

X-1.gif

でも、この列


//ポイントを取得
var oPoints = oObj.ActivePrimitive.Geometry.Points;


のGeometryにGetGeometry2(null, 0)を入れ替えると


//ポイントを取得
var oPoints = oObj.ActivePrimitive.GetGeometry2(null, 0).Points;


ポーズと関係なく元のジオメトリの位置を取得出来ます:

X-2.gif

null は フレーム数。 null にすると現フレームになります。
0 は siConstructionModeModeling なので、元のジオメトリを取得します。

以上。

SI | Memo : Filter PolyMeshes

スクリプトのメモです。


いつもポリゴンメッシュだけを取得してくれるファンクションを使っています。

何も考えずに書いたファンクションですけど。
8~9割は潤樹さんのパクリだし:

潤樹さんのファンクションは元々こんな感じです:


//jscript
function FilterX3D(in_Objs)
{
var oCol = XSIFactory.CreateObject('XSI.Collection');
for (var i=0; i {
if (in_Objs(i).IsClassOf(siX3DObjectID))
{
oCol.Add(in_Objs(i));
}
}
return oCol;
}


しかし俺はポリゴンメッシュだけが欲しい時が多い。
そして、ブランチ選択対応もしたいのでこう変えてみました:


//jscript
function FilterPolyMsh(in_Objs){
var oCol = XSIFactory.CreateObject('XSI.Collection');
oCol.unique = true;

for (var i=0, a=in_Objs.count; i < a; i++) {
if (in_Objs(i).type == "polymsh") {
oCol.Add(in_Objs(i));
}
}
var oExp = oCol.expand();
for (var i=0, a = oExp.count; i < a; i++) {
if (oExp(i).type == "polymsh") {
oCol.Add(oExp(i))
}
}
return oCol;
}


今見ると結構無駄が多いから、もっと効率のいいファンクションを書いてみました。
なんとなくですね。

ミリ秒ぐらいの差しか出ないけど、ほぼ毎回使うファンクションなので書きなおしてみました。


//jscript
function FilterPolyMsh(in_Objs){
var oCol = XSIFactory.CreateObject('XSI.Collection');
oCol.unique = true;
oCol.items = in_Objs;
return SIFilter(oCol.Expand(), siPolyMeshFilter);
}


なんかJSにしてはコンパクトでエレガントな感じですね。

これで決まりです。このファンクションはコレクションに入れておきます!

便利なファンクションやスクリプトメモを自分のフォルダに貯めていて、自分なりにジャンルで整理しています。
スクリプトを組む時に、例えば Current Camera が欲しいけどその取得はもう忘れてしまった!という時にこのスクリプト集フォルダを探って、「Get Current Camera.js 」のコードをコピペーするだけで済むから単純なスクリプトはすぐに書けます。

なので、なるべくこのスクリプト集に効率の良いスクリプトにしています。

VBS - JScript の For Loop Performance について

スクリプティングな話です。XSIとあんまり関係ないかもしれません。


baseで拾ったコードをどうやって速くなったかこれから少しだけ説明したいと思います。


まずは、XSI男を出して、コードをそのままでためしてみると:

54105 ミリ秒

使いたくないほどの遅さ。

コードを見てみると:

            if (angle >= hardEdgeLimit) then
               AddToSelection edgeName
            else
               RemoveFromSelection edgeName
            end if

エッジでループして、エッジを選択して、要らないエッジは RemoveFromSelection で選択削除されています。

通りで遅いじゃん!

これをコレクションに直してみて


set oCol = CreateObject("XSI.Collection" )
for each oEdge in oEdges
set oPolys = oEdge.NeighborPolygons
if (oPolys.Count = 2) then
dim angle, edgeName
angle = GetFacetAngle(oPolys(0), oPolys(1))
edgeName = oSelection.FullName & "." & oEdge.FullName & "[" & oEdge.Index & "]"
if (angle >= hardEdgeLimit) then
oCol.AddItems (edgeName)
end if
end if
next
selectObj(oCol)


ログをオフにして、


'Log Off
set oCmdLog = Dictionary.GetObject( "preferences.scripting" )
userPref = oCmdLog.cmdlog.value
oCmdLog.cmdlog.value = false


無駄なところを削除してみると:

24832 ミリ秒

2倍以上速くなった!


と考えると上出来だけど、24秒以上じゃあ遅いよね・・・。
こんなローポリのXSI男なのに。


でもしょうがない、とりあえずそのままJScriptに変えてみます。
PPGが欲しいし、VBSでプラグインを書く気にならない。


ついでに、この古い書き方
CreateObject("Sumatra\Scripting\Math\SIVector3")

を新しいXSIMath
XSIMath.CreateVector3()

に変える、10ミリ秒ぐらい速くなる気がします。

あとは、色々好きにアレンジして、PPGを付けてみました。

試してみたら・・・

JScript
998 ミリ秒

速い!なんだこの差!

期待以上の速さです。

なんでVBSの方がそんなに遅いのか・・・ちょっと考えてたら

まさかアレですか。
れいのアレですか!

あのDIMってやつ?

それしか思い浮かばないけど、こんな差はいくらなんでも大きすぎでしょう。

試してみます。

VBSコードをOption Implicit にして、変数を全部宣言して、あっちこっち修正してためしてみたら・・・

652 ミリ秒

速っ!

変数を宣言しただけでめちゃめちゃ速くなりますね。
宣言するとすぐどの変数かコードがすぐ分かるから速いですが、宣言しないとどこの変数か分からないから探し続けるからだそうです。

因みに、プロパティとかより、変数の方が速いです。
要するに、コードでオブジェクトのプロパティを何回も取得することより、
一回だけで取得して変数の値に変えておくと、
あとはその変数を呼ぶだけでいいから速いです。

まぁ、変数とかのことはこのブログじゃなくて、ちゃんとしたサイトで調べた方が良いかもしれなですw


しかし、なんで!?

なんでJScriptより速い?!

コードは一緒ですよ。

違いはループだけですね。
JScriptでは for ループを使っていて、VBSの方はfor each ループでやっています。
JScript には for eachがないからね、そして、for in はなぜか、XSIコレクションには使えません。

試しに VBSでも for ループでやってみたら:

793 ミリ秒

えっ、VBS の for ループは遅いんだ!

ていうか for each が速いかな。

速くなったのは嬉しいんだけど、PPG付きのJScriptより速くなるのは悔しい。
なので、JScriptの方も頑張ってみました。

速くはなったけど、VBSに負けてます:

JScript
810 ミリ秒


ポイントはfor ループです。

for (var i=0; i<Collection.count; i++)




for (var i=0, a = Collection.count; i<a; i++)


に変えるだけで100ms以上速くなりました。

これは、上にも書いたけど、変数を作っておくと速いです。

i< Collection.count とは、ループが一周した後に、コレクションの数を取得して、i の数値と比べるということです。
コレクションの数と比べているから、毎回コレクションの数を取得するということになります。

コレクションの数を変数にすると、毎回この変数の数値を使って確認するだけなので速いです。

これからもこのやり方で書きます!

JScript で Sleep / Timer

以前にJScriptからVBScriptのSleepを実行する方法を書いたんですけど、このやり方だと必ず2ファイルになってしまいます。アドオンとかだったら別に問題ないけど、スクリプトとプラグインだと面倒かもしれません。

さっきふと思ったんだけど、JScriptからそのSleep専用スクリプトを作って、実行して、消せばいいじゃない?

VBSを作っているからタイムラグが出て正確なタイマーにはなりません。
俺の実験では60~80ミリ秒のラグがあります。

function Sleep(seconds){
// Creating the VBS Sleep File in C:\
var FSO = new ActiveXObject("Scripting.FilesystemObject");
var VBSleep = FSO.CreateTextFile("C:\\vbsleep.vbs", true);
VBSleep.WriteLine("dim mult : mult = wscript.Arguments.Item(0)");
VBSleep.WriteLine("wscript.sleep 1000 * mult");
VBSleep.Close();

// Calling VBS Sleep
var WshShell = new ActiveXObject("WScript.Shell");
WshShell.run("C:\\vbsleep.vbs " + seconds, 2, 1)
FSO.DeleteFile("C:\\vbsleep.vbs")
}


しかしメリットとしては、待っている間にCPUが使われません。


因みに、ミリ秒を秒にしている時点で2ミリ秒ぐらいのタイムラグが出ます。


CPU使用率より正確なタイマが必要な時に、ミリ秒で while ループにすれば良いです。

JRの方とかかな?

とにかくこんな感じ:


function Sleep(seconds){
var StartTime = new Date();
var datet = 0;
while(datet < (seconds * 1000)){
var EndTime = new Date();
var datet = EndTime.getTime() - StartTime.getTime();
}
}


これでループして毎回 経過時間 - 開始時間 という計算して、目的時間と比べているからCPUの使用率がずっと100%のままです。最近のCPUはマルチコアだけど、スクリプトはマルチスレッドじゃないから1コアだけが使用率100%になります。なので、大抵の場合は特に影響がないかもしれません。

メリット: VBSleepより正確なタイマーです。
デメリット: Sleepしている間にCPU1コアを100%で使いまくります。

なので、スリープの時間が長いか、しょぼい1コアのPCでやる時にVBS-Sleepでやったほうが良いかもしれません。

なんか、無駄にCPU使用率を100%にするのは嫌です。
僕、エコですから。


プラグインを作る気があれば、TimerEvent を使えば良い。
メリット: 上記の方法より正確でCPUにやさしい。
デメリット: Softimage以外のソフトでは使えません。当たり前ですけど。プラグインかアドオンにしないと使えません。


タイマー機能としては微妙としても、なんかの参考になれば良いな~。

SI | GetBBox (BBox 取得)

今日はマニュアルを読んでたらGetBBoxというコマンドを見つけました。

このコマンドは知らなかったorz

知っていましたか?
早く教えてください!

アホなやり方だな~と思ったらコメントを宜しくお願いします。

こないだ書いてたスクリプトはもっと早く書けるし、実行時間も短くなります。

前回はポイントを取得して、全ポイントを比べて、最大と最小値を取得して、親オブジェクトの位置を足す。

というのはこのGetBBoxで一発で出来ます。

このGetBBoxはオブジェクトのバウンディングボックス情報を出してくれるスクリプトです。
つまりXYZの最高と最小値。

こんな感じです:

var oPoly = GetBBox(selection(0))


これで oPoly は6つの数値を出してくれます。

oPoly(0) = X 最小値
oPoly(1) = Y 最小値
oPoly(2) = Z 最小値
oPoly(3) = X 最高値
oPoly(4) = Y 最高値
oPoly(5) = Z 最高値


LowerBound と UpperBound というパラメーターもありますが、同じです。

例えば、

oPoly.value("LowerBoundX") は oPoly(0) と同じです。
oPoly.value("UpperBoundX") は oPoly(3) と同じです。

単語だから覚えやすいかもしれないけど。

これで前回書いたスクリプトが軽くなります。

DOWNLOAD

因みに、GetBBoxは複数のオブジェクトも可能ですが、子持ちのオブジェクトがある場合はその親オブジェクトだけが計算されます。子オブジェクトが無視されます。
Previous page Next page