【Softimage】JSON読み書きプラグイン

SoftimageからJSONの読み書きが行いたかったのでネットで配布されているJSONライブラリjson2.jsをSoftimageプラグイン化してみました。
これを使えばSoftimageコマンドとしてJSON形式のファイルが読み書きできるはず。
※json2.jsについては他のページをご覧くださいませ。
 http://codezine.jp/article/detail/6533とかが良いかも...

http://bit.ly/SI_JSON2からファイルをダウンロードしてください。

◆使い方◆

//書き込み
var JSON_base = {}
JSON_base.AAA = "bafbababa";
JSON_base.BBB = "uuu";
MakeJSON ( "c:\\Test.json", 2, JSON_base );
//読み込み
var JSON = ReadJSON("c:\\Test.json")
logmessage(JSON.AAA)

以上です。簡単ですね〜。(説明も簡潔すぎる)
これで情報のやり取りが簡単になるはず^^

内容としては本当にjson2.jsをコマンド内にぶち込んだだけ。
ちなみに、

var JSON = JSON2();

とすることで自分でJSONオブジェクトを作成することもできます。

Softimage的疑似クラスコマンド化について[Softimage Script][JScript]

先日会社でJScriptで疑似クラスを記述したJSファイルを見かけました。
その内容はエクセルを操作するための汎用的なクラスだったんですが
、他のスクリプトからはeval関数を利用してファイル内の文字列を読み込む形式だったんです。

特にそれ自体には問題ないんですけどね。
会社ではSIのワークグループをTortoiseSVNを使用して管理していたため、
eval関数を利用するとファイルがロックされていると認識されSVNからの更新に失敗してしまうなど、
食い合わせが悪かったのですよ。(=△=;

ということで、疑似クラスを自己インストール型プラグイン化できないかなーと試してみました。
今回使用するクラスは以下。犬クラスです。
※クラスについて詳しくはググってください〜

var oDog = new clDog("ポチ");//クラスをインスタンス化する
logmessage(oDog.name);
oDog.SetType("雑種");

//犬クラス
var clDog = function(in_name) {
	this.name = in_name;//犬の名前
	this.type = "";
 
	//犬種を設定しログを表示
	this.SetType = function( in_type ){
		this.type = in_type;
		logmessage(this.name + "の種類は" + this.type + "だよ!");
	}
}

今まではこのクラスが記述されているjsファイルを別のjsファイルから文字列として読み込むことで
汎用的なコマンドとして使用してたんですね〜。
ではでは、これを自己インストール型のプラグイン化してみます。

function XSILoadPlugin( in_reg )
{
	in_reg.Author = "";
	in_reg.Name = "clDog";
	in_reg.Email = "";
	in_reg.Help = 	"";
	in_reg.Major = 1;
	in_reg.Minor = 0;
	
	in_reg.RegisterCommand("clDog","clDog");//コマンド

	return true;
}

function clDog_Init( in_ctxt )
{
	//コールバックコンテキストからCommandオブジェクトを取得
	var oCmd = in_ctxt.Source;
	//ArgumentCollectionを取得
	var oArgs = oCmd.Arguments;
	
	//引数の設定(追加)
	oArgs.Add("in_name", siArgumentInput,"");

	return true;
}

function clDog_Execute( in_name )
{
	return new clDog(in_name); //クラスをインスタンス化したものを戻してやる

	//犬クラス本体
	var clDog = function(in_name) {
		this.name = in_name;//犬の名前
		this.type = "";
	 
		//犬種を設定しログを表示
		this.SetType = function( in_type ){
			this.type = in_type;
			logmessage(this.name + "の種類は" + this.type + "だよ!");
		}
	}
}

これで疑似クラスをプラグイン化したものが完成っす!
単純にコマンド内に疑似クラス本体を記述して、そのインスタンスを戻しているだけです。
これで、クラス内のメソッドに対して引数を渡す事もできます。
ただし、インスタンス化するための引数だけは自己インストール型の引数の記述をしています。

具体的な使い方は以下のような感じです。

var oDog = clDog("ポチ");//コマンドを実行した戻り値がクラスをインスタンス化されたもの
logmessage(oDog.name);
oDog.SetType("雑種");

以上です〜。

選択中のアニメーションクリップとシーン範囲を一致させるプラグイン

アニメーションの作業をしてて、アニメーションクリップのフレーム幅と、シーンのフレーム範囲を一致させることがよくあります。
前々から思ってたけど、毎回メンドクセ―よ!!(゚Д゚)ゴルァ!!ということでプラグイン作りました。

◆使い方◆
1.http://bit.ly/FrameMatch_from_AnimetionClipからファイルをダウンロード

2.解凍して C:\users\ユーザー名\Autodesk\Softimageのバージョン\Application\Plugins とかにファイルをぶち込む
  (パスは個人によって違いますので注意)

3.メインコマンドパネルの[Animation]メニュー内に『アニメーションクリップとシーンのフレーム範囲を一致』が追加される。

4.アニメーションミキサーのクリップを選択(複数可)

5.コマンド実行してフレーム範囲を一致させる。

『アニメーションクリップとシーンのフレーム範囲を一致』メニューは切り離しておくと使いやすいはず。
良く使う場合はキーボードショートカットに追加しておくのも良いかと。

コマンド名 動作
FrameMatch_from_AnimetionClip_S 開始Frを一致
FrameMatch_from_AnimetionClip_E 終了Frを一致
FrameMatch_from_AnimetionClip_SE 開始・終了Frを一致

本当はクリップを選択した状態で右クリックからコマンドを実行したかったんですが、SI2011では登録出来ないように見えました。
無念orz

ちなみに、デフォ機能にはないですよね??
あったら泣く。。。


※当プラグインを使用してなにか起っても責任とれませんが、そでも良い方だけどうぞー。

Softimageからテクスチャを直接外部ツールで起動するプラグイン『ImageOpener』

Softimageからテクスチャを直接開くツールを作りたいと思ってたら丁度良い具合に
XSI道場さんでTR OpenImageの記事(http://www.cveld.net/modules/xigg/index.php/node/990)を見かけたので、
同じような機能を持たせたプラグインを作りました。

ダウンロードは以下からどうぞ。
『ImageOpener( http://bit.ly/ImageOpener )』
zipになってるので解凍C:\users\ユーザー名\SIのフォルダ\Application\Pluginsとかにぶち込むと使えます。

大まかな使い方はTR OpenImageとおなじなので、そっちの動画を見てもらえれば大丈夫かと。

以下TR OpenImageからの変更点です。

 1.複数個選択→実行で複数起動可

 2.モデル選択時にAlt+右クリックでモデルに属しているテクスチャ全て起動(複数可)

 3.同名別拡張子のファイル、相対パス指定で開くオプションを実装

 4.JScriptで記述してるのでSI2010以前でも使える

 5.設定オプションが右クリックメニューから呼び出し可能

分かっている不具合等は

 1.モデルのテクスチャを全て開く場合は、RenderTree内のイメージノードが全て対象になります。
   その際、そのノードが使用されているかどうかという確認は行ってないので、
   宙ぶらりん(未接続)のテクスチャも開かれます。

 2.また、RenderTreeではマテリアルノードを選択状態でもメニューが出て、
   コマンドが実行出来ますが思いっきりバグります。

 3.画像ビューアなど開けないソフトもあります。

という感じです。

プラグインを使用してなにか問題が起きても責任はとれませんのでオッケーな人だけどうぞです。
しかし、ようやく普通に使えるツールを公開できたな…。

スクリプトエディタ拡張プラグイン

きっかけはjunkiさんのツイッタ―での『XSIのJScriptでファイルパスを書くときにフォルダ区切りは \ ではなく \\ にするわけですが、どっかからコピペしたパス文字列の \ を \\ に書き換えていく過程が実に苦痛なのですが、楽にする良いアイデアはないでしょうか?』という呟き。
良いお題だったのもあり、ツイッタ―では大いに盛り上がったようです。
最終的に示されたのは

というもの。バッチファイルを作ってクリップボードにコピーする際に、変換しちゃおうというものですねー。
うむ。合理的。

自分は盛り上がってる時間帯を逃してしまい、やっちまった感満点だったのですが、自分なりのアプローチでなにか出来ないか探ってみました。
で、辿り着いたのがSoftimageのスクリプトエディタ用のコマンドを作るというもの。
よくよく考えてみたらDCCツール作業者を対象にしたプラグインは数多くあれど、ツール作成者を対象としたスクリプトエディタを拡張するコマンドって見たことないんですよね。
スクリプトエディタが操作できるのかどうかも最初は良く分からなかったので、どうやったらいいのか小一時間悩みましたがなんとか出来ました。

プラグインファイルをどこかにアップすればいいと思うんですが、いい場所が良く分からなかったのでコードそのまま貼り付けます。
(何処か共同でまとめられたら使う側としても便利じゃないかなー??とか思ったり…。)

function XSILoadPlugin( in_reg )
{
	in_reg.Author = "mochio" ;
	in_reg.Name = "ScriptEditor_ExpansionMenu Plug-in";
	in_reg.Major = 1;
	in_reg.Minor = 0 ;

	//***メニュー登録
	in_reg.RegisterMenu( siMenuScriptEditContextID , "ScriptEditor_ExpansionMenu" );
	//***コマンド登録
	in_reg.RegisterCommand("ScriptEditor_ChengePathString_Clipboard","ScriptEditor_ChengePathString_Clipboard");
	in_reg.RegisterCommand("ScriptEditor_ChengePathString_SelectedText","ScriptEditor_ChengePathString_SelectedText");

	return true;
}

function XSIUnloadPlugin( in_reg )
{
	Logmessage( in_reg.name + " has been unloaded." );
	return true;
}

//--------------------------------------------------------------------------
// 右クリックメニュー登録部分
//--------------------------------------------------------------------------
//ポップアップメニューの登録
function ScriptEditor_ExpansionMenu_Init( in_ctxt )
{
	var menu = in_ctxt.source;

	menu.name = "ExpansionMenu(JScript)";
	
	//共通メニュー
	menu.AddCommandItem("クリップボードの文字列の「\\」を「\\\\」として貼り付け","ScriptEditor_ChengePathString_Clipboard");
	menu.AddCommandItem("選択範囲を「\\」を「\\\\」に変換","ScriptEditor_ChengePathString_SelectedText");
}

//--------------------------------------------------------------------------
// コマンド
//--------------------------------------------------------------------------
function ScriptEditor_ChengePathString_SelectedText_Init( in_ctxt )
{
	var oCmd;
	oCmd = in_ctxt.Source;
	oCmd.Description = "";
	oCmd.ReturnValue = true;
	return true;
}

//選択範囲を変換
function ScriptEditor_ChengePathString_SelectedText_Execute(){
	var oSEditor = LF_GetScriptEditor();
	var sSelText = oSEditor.GetAttributeValue("selectedtext");
	LF_StringPaste(sSelText);
}

function ScriptEditor_ChengePathString_Clipboard_Init( in_ctxt )
{
	var oCmd;
	oCmd = in_ctxt.Source;
	oCmd.Description = "";
	oCmd.ReturnValue = true;
	return true;
}

//クリップボードから貼り付け
function ScriptEditor_ChengePathString_Clipboard_Execute(){

	// クリップボードに文字列を設定
	var jp;
	if (!jp) jp = {};
	if (!jp.raindrop) jp.raindrop = {};
	if (!jp.raindrop.frog) jp.raindrop.frog = {};
	jp.raindrop.frog.clipboard || (function ()
	    {
	        // コマンドのID
	        var OLECMDID_COPY = 12;
	        var OLECMDID_PASTE = 13;
	        var OLECMDID_SELECTALL = 17;

	        // IE の初期化
	        var _internetExplorer = new ActiveXObject ('InternetExplorer.Application');
	        _internetExplorer.navigate ("about:blank");
	        while (_internetExplorer.Busy)
	            WScript.Sleep (10);

	        // textarea 要素を作成する
	        var _textarea = _internetExplorer.document.createElement ("textarea");
	        _internetExplorer.document.body.appendChild (_textarea);
	        _textarea.focus ();

	        jp.raindrop.frog.clipboard = {
	            // クリップボードに文字列をコピー
	            setText: function (text)
	            {
	                _textarea.innerText = text;
	                _internetExplorer.execWB (OLECMDID_SELECTALL, 0);
	                _internetExplorer.execWB (OLECMDID_COPY, 0);
	            },

	            // クリップボードより文字列を取得
	            getText: function ()
	            {
	                _textarea.innerText = "";
	                _internetExplorer.execWB (OLECMDID_PASTE, 0);
	                return _textarea.innerText;
	            },

	            // IE を解放
	            release: function ()
	            {
	                _internetExplorer.Quit ();
	            }
	        };
	    }());

	//クリップボード内の文字列を取得
	var GetStg = jp.raindrop.frog.clipboard.getText();

	jp.Quit;//開放…できてんのか???
	jp = null;
	
	LF_StringPaste(GetStg);
}

//文字列を変換してスクリプトエディタに貼り付ける
function LF_StringPaste(in_Stg){
	in_Stg = in_Stg.replace(/\\(?!\\)/g , "\\\\");
	in_Stg = in_Stg.replace(/\\\\\\/g , "\\\\");//後読み対応してないので、\\\となってしった場合に\\に戻す
	var oSEditor =  LF_GetScriptEditor();
	oSEditor.SetAttributeValue("inserttext" , in_Stg);
}

//スクリプトエディタを取得する
function LF_GetScriptEditor(){
	var l = Desktop.ActiveLayout;
	for(var i = 0; i < l.Views.count; i++){
		if(l.Views(i).type == "Script Editor"){
			var oSEditor = l.Views(i);
			return l.Views(i);
		}
	}
}

適当にファイルにコピーしてプラグインとして読まれるところに放り込んで下さい。
スクリプトエディタ上で右クリックをすると、専用のメニューExpansionMenu(JScript)が追加されています。

今回用意したのは
クリップボード上にある文字列(ctrl+cとかでコピーしておいたパス)の\を\\に変換した状態で貼りつける、というコマンド

既にスクリプトエディタ上にある文字列の中の\を\\に変換する、というコマンド

の2つです。
2つ目の選択範囲の文字列を変更するものに関しては、複数行も問題なくいけます。
右クリックからコマンドを実行するのは多少手間なので、コマンドをショートカットに登録しておくとよいかもしれません。

さらに拡張すれば、標準のスクリプトエディタにはない「選択範囲の置換」「正規表現を使った置換」といった機能も追加できそうな予感です。
しかし、何でこういう標準的にあってもよさそうな物が搭載されてないかな…。
是非オートデスク様にはこう言ったところも改善していただきたいところですね。

PPGのイベントを動的に追加する その2

前回の記事に続いて、『PPGのイベントを動的に追加する その2』です。
前回の記事では、最初からイベントの数が決まっている場合限定でしたが、今回はPPG内の他のパラメータによってイベントが増減する、というケースについて考えてみました。

で、とりあえず出来あがったのがコレ↓↓↓

function XSILoadPlugin( in_reg )
{
	in_reg.Author = "";
	in_reg.Name = "AutoEventFunction";
	in_reg.Email = "";
	in_reg.Help = 	"";
	in_reg.Major = 1;
	in_reg.Minor = 0;
	
	in_reg.RegisterCommand("AutoEventFunction","AutoEventFunction");//コマンド
	in_reg.RegisterProperty( "AutoEventFunction" );// プロパティ

	return true;
}

function AutoEventFunction_Init( in_ctxt )
{
	var oCmd;
	oCmd = in_ctxt.Source;
	oCmd.Description = "";
	oCmd.ReturnValue = true;
	return true;
}

function AutoEventFunction_Execute( io_Context )
{
	//選択オブジェクト内にカスタムプロパティ作成
	var oProp = Selection(0).AddProperty("AutoEventFunction",false);
	InspectObj(oProp,null,null,siLock);
	return oProp;
}

function AutoEventFunction_OnInit(io_Context )
{
	MakeParam();	//パラメータ作成
	PPG.Refresh();	//レイアウトの作成
	MakeLayout();
}

//パラメータの作成
function MakeParam(){
	var oProp = PPG.Inspected.Item(0);
	oProp.AddParameter2("iCount",siInt4,0);//ボタンの数を定義する
}

//レイアウトの作成
function MakeLayout( io_Context )
{
	var oProp = PPG.Inspected.Item(0);
	var oLayout = PPG.Inspected.Item(0).PPGLayout;
	oLayout.Clear();

	oLayout.AddItem("iCount","ボタンの数");

	//iCountパラメータの数値からボタンを生成
	for(var i = 1; i <= oProp.iCount.value; i++)var oItem = oLayout.AddButton( i ,"ぼたん" + i);
}

//ボタンの数のパラメータイベント定義
function AutoEventFunction_iCount_OnChanged(){
	MakeLayout();//レイアウト再描画
	PPG.Refresh();
}

//**************動的イベントの作成********************
try{
	var oProp = PPG.Inspected.Item(0);
	var sInclude = "";
	for(var i = 1; i <= oProp.iCount.value; i++){
		sInclude += "function AutoEventFunction_" + i + "_OnClicked(){logmessage(\"ボタン"+ i + "を押したよー!!\");}";
	}
	eval(sInclude);
	logmessage("AutoEventFunction:自動生成ボタンイベント関数を読み込んだよ!")
}catch( e ){
	logmessage("AutoEventFunction:PPGが表示されてなかったから自動生成イベント関数は読み込まなかった")
}
//******************動的イベントの作成END**********

適当なファイル名で保存して、C:\users\ユーザー名\Autodesk\Softimage_(バージョン)\Application\Plugins などプラグインが読み込まれる場所に適当に放り込んじゃってください。
その後、適当なオブジェクトを選択した状態で AutoEventFunction(); のコマンドを実行すれば、カスタムプロパティがオブジェクト内に出来ます。

↓こんな感じのカスタムプロパティが出現

前回と同じく、イベントの記述は事前に準備はしていません。
早速数値のパラメータを動かしてみて下さい。そうすると、ボタンが現れたと思います。
これは数値パラメータのイベントを先に仕込んでおいて、パラメータが変更される度にPPGを再描画、ボタンをパラメータの数だけ表示するようにしているためです。

//iCountパラメータの数値からボタンを生成
for(var i = 1; i <= oProp.iCount.value; i++)var oItem = oLayout.AddButton( i ,"ぼたん" + i);

通常であれば、ボタンだけ表示されて動作しないのですが、ボタンを押してみると「ボタン○を押したよー」とボタンに応じたイベントが起っております。
(単純な動作なので、あんまり凄みはないですけどね…)

この「他のパラメータによってイベントが追加される」という動作はAutoEventFunction_iCount_OnChanged関数内のPPG.Refresh();というところが一番のポイントになっています。
数値パラメータを変化させてみると、ログに「自動生成ボタンイベント関数を読み込んだよ!」と毎回表示されます。
これは、PPG.Refresh();を行うことで、Softimageがプラグインを再度読みに行ってると言うことです。
この挙動を利用しています。

整理すると、

  1. パラメータが変化、イベント処理が発生!!
  2. PPG.Refresh();でリフレッシュ!
  3. Softimageがプラグインファイルを再読み込みする
  4. 動的イベントの作成の処理が走り、数値パラメータの値からボタンイベントを自動生成する

ということですね。

動的イベントの作成の処理は

//**************動的イベントの作成********************
try{
	var oProp = PPG.Inspected.Item(0);
	var sInclude = "";
	for(var i = 1; i <= oProp.iCount.value; i++){
		sInclude += "function AutoEventFunction_" + i + "_OnClicked(){logmessage(\"ボタン"+ i + "を押したよー!!\");}";
	}
	eval(sInclude);
	logmessage("AutoEventFunction:自動生成ボタンイベント関数を読み込んだよ!")
}catch( e ){
	logmessage("AutoEventFunction:PPGが表示されてなかったから自動生成イベント関数は読み込まなかった")
}
//******************動的イベントの作成END**********

の部分です。

try{}catch( e ){}が入っているのは、追加の処理がPPGが表示されていることを前提に書かれているからです。
もっとちゃんとしてる書き方もあると思いますが、とりあえずこれで動くからOKです。
ちなみに、今回のPPGと別のPPGが同時に表示されている場合などは考慮してないので、PPGの名前やタイプを確かめる等の処理を入れておかないと何かしらエラーが出そうな気もします。

以上、「PPGのイベントを動的に追加する」でした。m(_ _)m

PPGのイベントを動的に追加する その1

Softimageのインストール方のカスタムプロパティのイベント処理を動的に追加する方法を考えてみました。

※SoftimageのPPG関連の解説についてはJunkiさんのブログの友愛シリーズで詳しく解説して下さっています。非常に分かり易く参考になる記事ですから即読みが吉ですよ!!

で、イベント処理というのはボタンを押した _OnClicked イベントや、値を変化させたときにおこる _OnChanged イベントですね。
このイベントは通常、プラグイン作成時にあらかじめ記述しておく必要があります。

function HOGEHOGE_Botton1_OnClicked(){
	logmessage("ボタン1を押したよー!!");
}
function HOGEHOGE_Botton2_OnClicked(){
	logmessage("ボタン2を押したよー!!");
}

という具合です。

このイベントはあらかじめボタンの数とかが決まっていて、さらに個数がそこまでではない場合には別に苦になるもんだいではありませんね。受け入れます。
しかし、似たような動作をするボタンの数が100個とか1000個あったり、なんてことがあったら対処しきれません!
常考えられる対処としては、あらかじめ最大イベント数を想定してイベントを先に仕込んでおくということですが、それにも限界があります。
というか、ぶっちゃけメンドクサイっす。(そんなケース稀ですが…)

なので、どうにか楽をしてイベントを動的に追加出来んもんかと、ツイッタ―やらなにやらで相談に乗ってもらいつつ検討した結果、JScriptevalメソッドに辿り着きました。
evalメソッドは文字列を引数として渡してやると、あたかもそのファイル内にその文字列を書きこんだかのような動作をしてくれます。
通常は汎用関数なんかを別ファイルから読み取って、ファイル内にぶち込んだりするときに使いますね。
今回はその応用という感じです。

//読み込み用文字列
var sInclude = "";
//イベント関数を文字列として追加していく
for(var i = 1; i < 1000; i++){
	sInclude += "function HOGEHOGE_Botton" + i + "_OnClicked(){logmessage(\"ボタン"+ i + "を押したよー!!\");}";
}
//文字列となったイベント関数を読み込む
eval(sInclude);

上記のような記述をしておくことで、擬似的に以下のような記述がファイル内にされているということになります。

function HOGEHOGE_Botton1_OnClicked(){logmessage("ボタン1を押したよー!!");}
function HOGEHOGE_Botton2_OnClicked(){logmessage("ボタン2を押したよー!!");}
function HOGEHOGE_Botton3_OnClicked(){logmessage("ボタン3を押したよー!!");}
						・
						・
						・
function HOGEHOGE_Botton1000_OnClicked(){logmessage("ボタン1000を押したよー!!");}

Softimageさんにはこんな風(イベント関数が1000個存在している様)にみえてるわけです。
実際にPPG作って試してみると動作してるのがわかります。
これでボタンが100個あろうが、1000個あろうが恐くありません!!
それに、スクリプトファイル内の記述もスッキリしますし、内部の処理が変わっても対処しやすいですね。
注意しなければならないのは、この処理をファイルのグローバルの位置に書くということです。
通常のイベント関数と同じで、他の関数の中(ローカル関数)になってしまっていたら、きちんと動作してくれません。

さらに「PPGの他のパラメータによってボタンの数が変化する」なんてケースにも対応できないか考えてみました。
上手くいったのですが、長くなるので今回の記事はここまでとします。m(_ _)m