2009年から仕事でMAYAを勉強。MAYAはデフォルトでは使い勝手が悪い。逆にスクリプトを覚えてカスタマイズするとかなり強力なツールになることがわかってきた。

物覚えが悪いので勉強したことはリマインダとしてココに書き留めるようにする。

2009年5月29日金曜日

外部ファイルの存在チェック

外部ファイルを読み込もうとしてファイルがないとMAYAがハングするので回避コード。

俺はこうやった。

$fileExCheck = `filetest -f $fname`;

if ($fileExCheck == 0)
   {
      print "ファイルが見つかりません\n";
   }
   else
   {

      int $fid = `fopen $fname "r"`;
      ファイルが開いた後の処理をする
   }


一方プログラマーからもらったサンプルはこうだった。


// リストファイルを開く
int $fid = `fopen $fname "r"`;
   // 開けた?
   if( $fid != 0 )
   {

   ファイルを開いた後の処理をする
   }

   else
   {
   // ファイルが開けなかったエラー
   print ("ファイルを開けませんでした。"+$fname + "\n");

   }



おかしいな、俺がやるとint $fid = `fopen $fname "r"`; の時点でハングするのに・・・
これでいいならわざわざファイルチェック専用のルーチン作らなくていいからシンプルだよね。



.

ラジオボタンの謎 2

昨日は解決した(というか、正解ではないが回避策がわかった)ラジオボタン問題。

mixiのコミュニティで正しい解決法を聞いてみようと思いうまくいかない例
作ってみたが、なぜかうまく動いてしまう。

いろいろ試すうちに”ウィンドウをたくさん作って消す”ような動作をさせていると
おかしくなる事がわかった。

つまり“MAYA操作中にできるゴミデータが変数に残っている”のが原因じゃなかろうか?

★★★★これでウィンドウをたくさん出しては消し、とやるとおかしくなる★★★★

global proc makePrimitive()
{
global string $radioCollection1;
string $selected;

$selected = `radioCollection -q -select $radioCollection1`;
if($selected == "rB1")
{
   polySphere;
}
else if($selected == "rB2")
{
   polyCube;
}
}

global proc btn()
{
   string $windowName = `window -title "test"`;
   columnLayout;
   text -label "make Primitive";
   string $radioCollection1 = `radioCollection`;
   radioButton -label "make Sphere" -select rB1;
   radioButton -label "make Cube" rB2;
   button -label " Aplly " -command "makePrimitive()";
   showWindow;
}

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

なので変数をクリアするおまじないを入れてみた。

★★★★★★★★おまじないをいれた改良版★★★★★★★★

global proc makePrimitive()
{
global string $radioCollection1;
string $selected;

$selected = `radioCollection -q -select $radioCollection1`;
if($selected == "rB1")
{
   polySphere;
}
else if($selected == "rB2")
{
   polyCube;
}
}

global proc btn()
{
   global string $radioCollection1;
   $radioCollection1 = "";

   string $windowName = `window -title "test"`;
   columnLayout;
   text -label "make Primitive";
   string $radioCollection1 = `radioCollection`;
   radioButton -label "make Sphere" -select rB1;
   radioButton -label "make Cube" rB2;
   button -label " Aplly " -command "makePrimitive()";
   showWindow;
}

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★


いまのところうまく動作しているようだ。昔作って今動かなくなっている別の
スクリプトにも組み込んでみたが正しく動作した。

どうやらこれが正解のようだ。 こういう仕様はアレかね、動的な扱いができるスクリプトで
あるが故に、ある物体の名前が同じならそれは絶対同一の物体であることを保証できないっつう
ことですかねえ。

なんにせよ変数の初期化やウィンドウの存在確認は懇ろにやっておくにこしたことはないようだ。


.

2009年5月28日木曜日

ちょっと寄り道・・・ラジオボタンの謎

何気なく昔作ったラジオボタンを組み込もうとしたら動かない。なぜかサンプルも動かないものがある。2008で仕様が変わってる?原因究明中

→ウィンドウ生成時にはradioCollection4という名前のラジオコレクションだったのに次のプロシージャに飛んだときにはradioCollection5になってる。

→なぜか自分の環境だとradioCollection命令を出すたびに
radioCollection5、radioCollection6・・・と増えていく。なので次のプロシージャでradioCollectionを発行したら名前が違ってしまう。

→なにかを何かしてしまうと上記症状が起こるらしい。でも”何か”がわからん・・・

→新しいソースをゴチャゴチャいじらないで昔ちゃんと動いていたバージョンで原因を探るほうがいいな・・・



→原因が判明。ラジオボタンを表示するウィンドウをプロシージャにしてしまうと、次にradioCollectionを呼び出した時にラジオコレクション名を探し出すことができず、仕方ないので新しいラジオコレクションを生成してしまうらしい。
(05/28  ↑は見当違いであることが判明)

そういえばサンプルコードも全部プロシージャじゃない生のスクリプトになってるわ。

でもこれだとスクリプトフォルダにおいてコマンドで呼び出すことが出来ないので仕様として不自然。なにか解決方法があるはずなのでネットで他のサンプルを漁ることにする。


これだけで7時間も悩んでたよ…



.

汎用パーツをLibみたいにする 2

---------------------------------------
そういえば、もらったサンプルが返す値は変数1個だけだんだけど、配列2個とか複数の値を
返したい場合はどう記述すればいいのんか?

return $val1[] $val2[];

とか?
---------------------------------------

の続き。

えーまず用語がおかしいですね。関数に対して渡すのは引数、戻ってくるのは返り値
こういうの間違ってると誰かに相談する時意味が伝わらないので注意しないと。ついでに「固執」は“こしつ”でも“こしゅう”でもOK。でも「破綻」は「はじょう」ではなく「はたん」

話がそれまくり。

ということで、昨日の上の書き方ではsyntax errorでダメ。で、どうすればいいか寝ながら考えた。

1)引数みたいに()でくくる
2)2つの返り値を2つの配列で返すのではなく1個の二次配列にする

1)は違う気がするなあ。2)は…floatとstringの混在なんてできるんだろうか。普通は(x,y,z;x2,y2,z2)とかだよな。


.

2009年5月27日水曜日

汎用パーツをLibみたいにする

一区切りついたのでソースを見やすくする一環の

”汎用ルーチンを別に作って返り値をもらう”の研究。

プログラマからサンプルをもらったので見る。考えるな、盗むんだ。

proc int[] GenerateIndex( int $num )
{
int $newval[];
int $cnt;

// num <= 0 の場合は最低一つ返すようにする
$newval[0] = 0;

for( $cnt=0 ; $cnt<$num ; $cnt++ )
{
$newval[ $cnt ] = $cnt;
}

return $newval;
}

・・・なんだreturnって。はじめて見るぞ。

とにかくforとセットになる命令であることは確かなようだ。これで$newvalの値を返すのか?

返す場所は指定しなくても来た場所に戻るのか?

で、そもそもどこからこれを呼んでいるのか。

// 8個の連続したインデックス配列を作る
int $idxs[] = GenerateIndex(8);
int $idx;

ぬお、変数に直接プロシージャ名を指定してる。こんなことができるのか。



これを参考に試行錯誤した結果、俺のやりたい事はこうすればできると判明

1.サブルーチン的なglobal procをまとめたファイルをhogehogelib.melという
名前で保存しスクリプトフォルダに入れる。

ここで重要なのは hogehogelib.mel の最後に

global proc hogehogelib()

と書いておくこと。

2.適当なスクリプトを書く。そのなかでhogehogelib.melを使いたい場合は1行
hogehogelib;
と書いておきスクリプトをロードさせる。

あとはそのプロシージャの中で、libにあるプロシージャを呼び出せばreturn命令で
戻ってきてくれる。


そういえば、もらったサンプルが返す値は変数1個だけだんだけど、配列2個とか複数の値を
返したい場合はどう記述すればいいのんか?

return $val1[] $val2[];

とか?







今日のスクリプト格言


”変数名のタイプミスには気をつけろ。変数だから no syntax error”


2009年5月26日火曜日

見やすいコーディングを考える

初心者も初心者なりに、コーディングの可読性に悩むわけですよ。


・変数がどんどん増えて、どの関数がどこで使われているのかわかりにくくなってくる。いや無駄な変数はないんだけど、無駄がなくても多いので困る

・インデントはどういうルールで使うのが一番見やすい?

・汎用のルーチン(配列から任意の部分を切り取って新しい配列に入れるとか)はそれ単独でグローバルプロシージャにして返り値をもらうようにすればファイル毎のコードは小さくなる

・forとかループが入り組んでくると、どの[ とどの ] が対応してるのかわかんなくなってくる。ここは慣れ?今はいちいち// end of hogehogeってつけてる(だから余計に見づらい)



余談だけど、このブログもタブによるインデントが効かないからコードが相当見づらいよな…
とりあえずの結論として、ワードパッドでのコーディングは初心者といってもやっぱツライわ…





2009年5月25日月曜日

skinClusterの名前を引っ張る 2

さらにネットで調べたところ、もっとダイレクトなのを発見

$gaga = `ls -sl`;
findRelatedSkinCluster $gaga[0];

// 結果: skinCluster3 //

そういう命令があるかどうか不明という条件で調査するとこんなにも時間がかかるもんなのか・・・
(プログラマーには”そんなもんだ”といわれた^^;)

2009年5月23日土曜日

skinClusterの名前を引っ張る

金曜の時点でskinClusterの名前が取得できずにとまっていたが、解決の兆しあり。
昨日はオブジェクトを選択している前提で、そのオブジェクトに付属しているクラスターの名前をどうやったら引っ張れるのか悩んでいたが、前提が間違いのようだ。

(pCube1というものがあって、その中にpCube1.skinCluster1みたいにぶら下がっているものと推定していた)

MAYAレイアウト右側のチャンネルリストから入力に表示されているスキンクラスターの「入力」からスキンクラスターを選択し、ls -slすると

// 結果: skinCluster1 pCube1 //

おおう、マジかよ。取得できてるじゃん。そしたら

skinPercent -nrm false -tv joint1 0.3 skinCluster1 pCube1.vtx[0];

のところにそのまんま変数で代入すればいいんだ!




2009年5月22日金曜日

【MEL】頂点ウェイトの値から端数を取る 3

昨日頂点単位で値を丸めるところはできたので、今日はそれを選択中オブジェクトの全頂点に適用する部分の追加をする。

昨日のうちにちょっとだけ調査は済んでいる。

//選択中オブジェクトの名前を取得。まあ基本中の基本
$Oname = `ls -sl`;

//選択中オブジェクトの頂点個数を調べて変数に(これでforループを回す)
//※頂点リストは必ず順番に数字が振られ、歯抜けになることはないらしい
//※$AmtObjVtxはfor文に使う
//※$val3はpCube1.vtx["*"]の*に代入して頂点を指定
{
string $AmtObjVtx[] = `ls -fl $Oname.vtx["*"]`;
int $val3 = `size $AmtObjVtx`;
}

ls -fl [オブジェクト名] で頂点の名前を取得できる。同時に個数をsizeで調べる。

--------------------------------------
昨日はここまで
--------------------------------------

が、昨日はオブジェクトをls -slで取得して、その変数を使って頂点数を調べようとしていたのがエラーが出て困っていた。要するに

string $AmtObjVtx[] = `ls -fl $Oname.vtx["*"]`;
// エラー: line 1: string[] 型のデータを string 型に変換できません。 //

となるのだ。これがやっと解決した。


なんのことはない、ls -slで取得される文字列は、オブジェクトが1個だったとしても配列データなのだ。
試しにwhatIsを使ってみると

whatIs "$Oname";
// 結果: string[] variable //

うん、配列です。ぐおー聞いてないよ~~~~


だから文字列にするには配列の任意の部分をとりだしてstring配列に入れなおさなければならない。

$Oname = `ls -sl`;
//ls -slで取得される文字は配列なので、その1個目を指定して文字列変数に入れなおす
string $Oname2 = $Oname[0];

これでとりあえずオブジェクトの名前を1個、文字列として取り出す部分は解決。これを使ってそのオブジェクトの頂点名の配列数でforを回す。以下のようになるはず

//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
{
//選択中オブジェクトの名前を取得
$Oname = `ls -sl`;
//ls -slで取得される文字は配列なので、その1個目を指定して文字列変数に入れなおす
string $Oname2 = $Oname[0];

//オブジェクトの配列を取得
string $AmtObjVtx[] = `ls -fl pCube1.vtx["*"]`;
//これのpCube1を配列に置き換えればよい

//そのオブジェクトの頂点を1つずつ選択していくforループを作る

for ( $val in $AmtObjVtx )
{
select $val;
print ($val+ "\n") ;
}
}

★------------------ 完成 --------------------★

//start proc
{

//選択中オブジェクトの名前を取得
$Oname = `ls -sl`;
//ls -slで取得される文字は配列なので、その1個目を指定して文字列変数に入れなおす
string $Oname2 = $Oname[0];

//オブジェクトの頂点リストを取得
//オブジェクト頂点リストのpCube1.vtx["*"]を$Oname + ".vtx["*"]"とやろうとしても
//[*]が数値型のため上手くいかないので1文字ずつバラでくっつけて別の変数に格納する
$Vtxname = $Oname2 + ".vtx"+"["+ "*"+ "]";

//くっつけた文字列変数でオブジェクトの頂点名を配列に格納
string $AmtObjVtx[] = `ls -fl $Vtxname`;


//そのオブジェクトの頂点を1つずつ選択していくforループを作る

for ( $val in $AmtObjVtx )
{

//頂点選択する
select $val;

//★★★ここに昨日の丸め処理をつっこむ★★★

}





2009年5月21日木曜日

【MEL】頂点ウェイトの値から端数を取る 2

とりあえずウェイト値をひっぱって丸め、頂点に書き戻すところまでのサンプルが完成

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
//ウェイト値を引っ張ってきて小数点以下1位までに丸める処理

{
$testval = `skinPercent -query -value skinCluster1 pCube1.vtx[6]`;

//ウェイトを”値”として操作するのに一時代入する変数を作る
float $wgtvalue;
//最後の1回の例外処理を回すための条件判断用変数
int $numloop = `size $testval`;
//最後の1回はこの値から今までの値を引き算して求める
float $wgtamount = 10; //小数点以下2位まで残すならここが100
//最後に書き戻す用配列
float $pointwgt[];
$count = 0;

for ( $val2 in $testval)
{
$numloop--;

//print $count;
if ( $numloop >0)
{
$wgtvalue = $val2;
//print ($wgtvalue + "\n");
$wgtvalue += 0.05; //小数点以下2位まで残すならここが0.005
$wgtvalue = $wgtvalue *10; //小数点以下2位まで残すならここが100
$wgtvalue = `floor $wgtvalue`;
//
$wgtamount = ($wgtamount - $wgtvalue);
//
$wgtvalue = $wgtvalue /10; //小数点以下2位まで残すならここが100 。ここの値が現在の頂点のウェイト値になる
$pointwgt[ $count ] = $wgtvalue;
print ("\n配列の中身"+ $count + "\n" + $pointwgt[ $count ] + "\n");
$count++;
}
else
{
// print ($wgtamount + "\n");
$wgtamount = $wgtamount /10;//小数点以下2位まで残すならここが100。ここの値が現在の頂点の最後のウェイト値になる
// print $wgtamount;
$pointwgt[ $count ] = $wgtamount;
print ("\n配列の中身"+ $count + "\n" + $pointwgt[ $count ] + "\n");
}
}

//チェックのため配列の中身を表示する
print "\n\n仮配列$pointwgtの中身\n";
print ($pointwgt[0] + "\n");
print ($pointwgt[1]+ "\n");
print ($pointwgt[2]+ "\n");
print "$pointwgtのサイズ\n";
print (`size $pointwgt`+ "\n\n");

//配列をウェイトに書き戻す処理

//とりあえず頂点6番の修正ウェイトを頂点0番に入れる
int $val;
$count2 = 0;
$count = 1;


for ( $val in $pointwgt )
{
$jname = "joint" + $count;
skinPercent -nrm false -tv $jname $pointwgt[$count2] skinCluster1 pCube1.vtx[0];
}

}

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
次は決め打ちにしているジョイントの名前や頂点の番号を、選択したものから変数でもってこないといけない。

頂点を1個選択すると
select -r pCube1.vtx[4] ;

と出るので頂点番号は問題なかろう。ウェイトがつけられているジョイントの名前は

skinPercent -query -t skinCluster1 pCube1.vtx[0];

-tオプションで取得可能なことを発見

頂点を選択して以下を実行するとその頂点と関連のあるジョイント名を$skintrans配列に格納してくれる。
{
$selectvtx = `ls -sl`;
string $skintrans[] = `skinPercent -query -t skinCluster1 $selectvtx`;
print $skintrans;
}


skinPercent -nrm false -tv joint1 0.5 skinCluster1 pCube1.vtx[0];

↑の joint1 は $skintrans で置き換え、pCube1.vtx[0] は $selectvtxで置き換える。



{
$selectvtx = `ls -sl`;
string $skintrans[] = `skinPercent -query -t skinCluster1 $selectvtx`;
skinPercent -nrm false -tv $skintrans 0.5 skinCluster1 $selectvtxl;
}

これで“選択中の頂点の、1番目のジョイントに紐付けられているウェイトが0.5に書き換わる”。

{
$selectvtx = `ls -sl`;
string $skintrans[] = `skinPercent -query -t skinCluster1 $selectvtx`;
$cnt = 0;
for ($val in $skintrans)
{
skinPercent -nrm false -tv $skintrans[ $cnt ] 0.5 skinCluster1 $selectvtx;
$cnt++;
}
}

こうすると選択頂点にウェイトつけられている全てのジョイントに対するウェイト値に0.5が格納される。
あとはこれを元のスクリプトに合体、0.5のところが配列からとってこれれば最初の段階クリア

★★★★★★★★★★★★★★ 完成 ★★★★★★★★★★★★★★★★★★★


//ウェイト値を引っ張ってきて小数点以下1位までに丸める処理

{

////////////////////////////////////////
//変数の宣言
////////////////////////////////////////

//現在選択されている頂点の名前を取得
$selectvtx = `ls -sl`;

//選択中頂点と関連付けられているジョイント名を配列で取得
string $skintrans[] = `skinPercent -query -t skinCluster1 $selectvtx`;

//ウェイトを”値”として操作するのに一時代入する変数を作る
float $wgtvalue;

//最後の1回の例外処理を回すための条件判断用変数
int $numloop = `size $testval`;

//最後の1回はこの値から今までの値を引き算して求める
float $wgtamount = 10; //小数点以下2位まで残すならここが100

//最後に書き戻す用配列
float $pointwgt[];

////////////////////////////////////////
//スクリプト本体
////////////////////////////////////////

//丸め処理

$count = 0;

for ( $val2 in $testval)
{
$numloop--;

if ( $numloop >0)
{
$wgtvalue = $val2;
$wgtvalue += 0.05; //小数点以下2位まで残すならここが0.005
$wgtvalue = $wgtvalue *10; //小数点以下2位まで残すならここが100
$wgtvalue = `floor $wgtvalue`;
//
$wgtamount = ($wgtamount - $wgtvalue);
//
$wgtvalue = $wgtvalue /10; //小数点以下2位まで残すならここが100 。ここの値が現在の頂点のウェイト値になる
$pointwgt[ $count ] = $wgtvalue;
//print ("\n配列の中身"+ $count + "\n" + $pointwgt[ $count ] + "\n");
$count++;
}
else
{
$wgtamount = $wgtamount /10;//小数点以下2位まで残すならここが100。ここの値が現在の頂点の最後のウェイト値になる
$pointwgt[ $count ] = $wgtamount;
print ("\n配列の中身"+ $count + "\n" + $pointwgt[ $count ] + "\n");
}
}

//丸めた値の配列をウェイトに書き戻す処理

$cnt = 0;
for ($val in $skintrans)
{
skinPercent -nrm false -tv $skintrans[ $cnt ] $pointwgt [ $cnt ] skinCluster1 $selectvtx;
$cnt++;
}

}






2009年5月20日水曜日

【MEL】頂点ウェイトの値から端数を取る

今日から暫くのお題は「頂点ウェイトの値から端数を取る」
ゲームでは特に端数が困る場合があるのと、MAYAでウェイト値を修正しようとするととんでもない労力がかかるのでボタン一発で端数が消えるようにしたい。

モデルは専門外なので基本的なところを調査。まずpCube1を出してジョイントにスムースバインド。

任意のモデルの任意の頂点を選択するコマンド
select -r pCube1.vtx[0]

その頂点のウェイト値を変更すると以下のコマンドが履歴に出る
skinPercent -tv joint2 0.3 skinCluster1 pCube1.vtx[0];

頂点0番のウェイト情報を調べるには以下
skinPercent -query -value skinCluster1 pCube1.vtx[0];
// 結果: 0.5 0.448465 0.0515349 // ←こんな感じで出る

自動で頂点ウェイトが正規化されてしまうと操作できないのでノーマライズを一旦オフ
skinPercent -nrm false -tv joint1 0.5 skinCluster1 pCube1.vtx[0];
//-nrm = -normarize デフォルトはtrue

--------------------------------------------
とにかく1個の頂点でやりたい事ができれば、あとはループで全部回すだけのはず。


$testval = `skinPercent -query -value skinCluster1 pCube1.vtx[0]`;
こうすると$testvalにウェイトの値が入る。

for ( $val in $testval)
{
print ($val + "\n");
}
0.4586304275
0.4586304275
0.08273914506

おお、配列として格納されている。これを四捨五入して書き戻すだけで合計1のきれいな数値になるはずだんが。

ネットで調べた、俺レベルで一番理解しやすかったのは
”0.005を足して、100倍してfloorで小数点以下を切り捨てて100で割る”
これいってみよう。例によってまずベタ書きする。

{
$testval = `skinPercent -query -value skinCluster1 pCube1.vtx[0]`;
float $wgtvalue;

for ( $val2 in $testval)
{
$wgtvalue = $val2;
print ($wgtvalue + "\n");
$wgtvalue += 0.005;
$wgtvalue = $wgtvalue *100;
$wgtvalue = `floor $wgtvalue`;
$wgtvalue = $wgtvalue /100;
print ($wgtvalue + "\n");
}

}

★★★結果↓★★★
0.4025395377
0.4
0.4025395377
0.4
0.1949209247
0.19

ってオイ!合計が1にならんよ!! 合計が1なら四捨五入しても1になると思ってたorz 
というわけで1にならない場合の処理か、必ず1になる処理を考えないとイカン.
 ちなみに要素数が3個とは限らないので底も考慮しなくてはならない。個数はsizeで調べればわかるので、最後の1個は四捨五入せずに(1-それまでの計算結果の合計)としないといけない。


★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
//ウェイト値を引っ張ってきて小数点以下1位までに丸める処理

{
$testval = `skinPercent -query -value skinCluster1 pCube1.vtx[1]`;

//ウェイトを”値”として操作するのに一時代入する変数を作る
float $wgtvalue;
//最後の1回の例外処理を回すための条件判断用変数
int $numloop = `size $testval`;
//最後の1回はこの値から今までの値を引き算して求める
float $wgtamount = 10; //小数点以下2位まで残すならここが100
//最後に書き戻す用配列
float $pointwgt[];
$count = 0;

for ( $val2 in $testval)
{
$numloop--;

//print $count;
if ( $numloop >0)
{
$wgtvalue = $val2;
//print ($wgtvalue + "\n");
$wgtvalue += 0.05; //小数点以下2位まで残すならここが0.005
$wgtvalue = $wgtvalue *10; //小数点以下2位まで残すならここが100
$wgtvalue = `floor $wgtvalue`;
//
$wgtamount = ($wgtamount - $wgtvalue);
//
$wgtvalue = $wgtvalue /10; //小数点以下2位まで残すならここが100 。ここの値が現在の頂点のウェイト値になる
$pointwgt[ $count ] = $wgtvalue;
print ("\n配列の中身"+ $count + "\n" + $pointwgt[ $count ] + "\n");
$count++;
}
else
{
//print ($wgtamount + "\n");
$wgtamount = $wgtamount /10;//小数点以下2位まで残すならここが100。ここの値が現在の頂点の最後のウェイト値になる
//print $wgtamount;
$pointwgt[ $count ] = $wgtvalue;
print ("\n配列の中身"+ $count + "\n" + $pointwgt[ $count ] + "\n");
}
print "\n\n配列の中身\n";
print ($pointwgt[0] + "\n");
print ($pointwgt[1]+ "\n");
print ($pointwgt[2]+ "\n");
print `size $pointwgt`;


//配列をウェイトに書き戻す処理

//これじゃだめ。3つのウェイトの1番目に3回代入してるだけになってる
int $val;
$count = 1;
$jname = "joint" + $count;

for ( $val in $pointwgt )
{
skinPercent -nrm false -tv $jname $pointwgt[$val] skinCluster1 pCube1.vtx[4];
$count++;
}

}
}

$testval = `skinPercent -query -value skinCluster1 pCube1.vtx[4]`;

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
これでウェイトを丸めて配列に格納するところまでDONE
しかし頂点に戻せない!


続く






2009年5月19日火曜日

【MEL】複数の配列を格納、読み込み

前回の記事で配列をファイルに読み書きするところはできたので次の段階。これを適用したい元のスクリプトは

変数A
配列B

の2つの保存したい要素があるので、これをちゃんと切り分けて読み書きできないといけない。
とりあえず変数Aは1個しかないのは確定なので変数Aと配列Bをただ直列に並べて

“一つ目の文字列は変数A、2つ目以降は配列”

という決まりごとを設定してしまえばいいと考える。

まず書き込み。


{
string $center = "777";
string $str[] = { "aaa", "bbb", "ccc", "ddd" };
string $fname = "C:/onogu2/test.txt";

int $fid = `fopen $fname "w"`;

//まず1個目に文字列変数$centerを格納
$val = $center+="\n";
fprint $fid $val;

//2個目以降に$strを格納
string $val;

  for( $val in $str )
  {
  $val+="\n";
  fprint $fid $val;
  }

fclose $fid;
}

_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
次に書き込み。しかしうまくいかない…


{
string $str[];
string $center;
$fname = "C:/onogu2/test.txt";
$fileId=`fopen $fname "r"`;
$nextLine = `fgetline $fileId`;

//ファイルの1行目を$centerに格納
$center=$nextLine;
//試しにロケータを作ってみるが、printで中身はちゃんと表示されるのに名前のないロケータになってしまう…
print $center;
spaceLocator -name $center;

//2行目以降を配列に入れたいが1行目から配列に格納されてしまう
int $cnt = 0;
while ( size( $nextLine ) > 0 )
{
$str[ $cnt ] = $nextLine;
$nextLine = `fgetline $fileId`;
$cnt++;
}

fclose $fileId;

print $str;
  for ($val in $str)
  {
  spaceLocator -name $val;
  }

}





【MEL】ファイル(配列)読み込み

ファイルに書き込んだ文字列を読み出す処理。
昨日もらった以下の構文を勉強する

★★★★文字列配列読み込み★★★★

{
string $str[];
string $fname = "C:/onogu2/test.txt";

int $fid = `fopen $fname "r"`;

string $val;
int $cnt = 0;

  for( $val = `fgetline $fid` ; size($val) > 0 ; $val = `fgetline $fid` )
  {
  $str[ $cnt ] = $val;
  $cnt++;
  }

  for( $val in $str )
  {
  print $val;
  }

fclose $fid;
}

_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/

for( $val = `fgetline $fid` ; size($val) > 0 ; $val = `fgetline $fid` )
ここのところが自分にはどうにも難解。

  //$val = `fgetline $fid`はfor文の最初に1回だけ行う処理
  //2回目からはsize($val) > 0で文字列がなくなるまで回す
  //文字列がなくなったら$val = `fgetline $fid`の処理を行う

となっているらしいんだけど、以下でも同じ?


{
//見やすくするために変数はあえて動的に宣言
$fname = "C:/onogu2/test.txt";
$fileId=`fopen $fname "r"`;
$nextLine = `fgetline $fileId`;

while ( size( $nextLine ) > 0 )
  {
  print ( $nextLine );
  $nextLine = `fgetline $fileId`;
  }

fclose $fileId;
}

と、whileで文字列(size( $nextLine ))がなくなるまで繰り返す処理ではダメなんかな?スクリプトエディター上では同じ結果を返すんだけど。

文字列を配列$strに取り込む部分をくっつけると以下のような感じ。

string $str[];
$fname = "C:/onogu2/test.txt";
$fileId=`fopen $fname "r"`;
$nextLine = `fgetline $fileId`;
int $cnt = 0;

while ( size( $nextLine ) > 0 )
  {
  print ( $nextLine );
  $nextLine = `fgetline $fileId`;
  $str[ $cnt ] = $nextLine;
  $cnt++;
  }

fclose $fileId;
print $str;

ただ、いずれの場合でも排列には改行コード込みの文字列が代入されているようで、これだと困るかもしれない。for構文とwhile構文の差はいずれわかると思うので先にそっちの問題に進む。(とりあえずwhileを選択)

上のスクリプトに以下を追加

for ($val in $str)
  {
  spaceLocator -name $val;
  }



おおっ!ちゃんと名前のついたロケータが生成されるね!オブジェクトの名前にするぶんには問題なさそう・・・と思ったらなんかおかしい。配列の中身は"aaa","bbb","ccc"なのに、生成されるオブジェクトの名前は
"bbb","ccc","locator1"
で1個ずれてしまう。

試しに文字列を取り出すファイルの先頭に強引に1行書き加えてみる。
"aaa","bbb","ccc","locator1"

になった。つまり配列の個数は正しいが文字列が1個ずれて格納されているらしい。

プログラマにもらった構文にロケータ生成部分をくっつけたらちゃんとできた。これがforとwhileの差か!?いやいや、多分自分の書き方がおかしいに違いない。引き続き調査

わかった!!

while ( size( $nextLine ) > 0 )
  {
  $nextLine = `fgetline $fileId`;
  $str[ $cnt ] = $nextLine;
  $cnt++;
  }

ここで1行目を$strに代入するより先に$nextLineに次の行の文字列が代入されている。順番の問題だった。

while ( size( $nextLine ) > 0 )
  {
  $str[ $cnt ] = $nextLine;
  $nextLine = `fgetline $fileId`;
  $cnt++;
  }

これでOK!







XSI風味のプロジェクトマネージャー


MAYAのカレントプロジェクトの仕様はXSIから来るとどうにも使いづらい。なのでXSI風のプロジェクトマネージャーを作成。ボタンを押せばカレントプロジェクトディレクトリが変わります。

見ればわかるとおり7個分のDBをベタ記述(汗)。そのうち直せればいいなあ。



//プロジェクトマネージャー

//ディレクトリと名称を変数宣言
//ここのパスを書き換えるだけで6つまでプロジェクトリストを登録できます(7つ以上はプロシージャとボタンの追加が必要)
//★注意★MAYAはパスの区切り記号の"\"を認識できません(よう気がする)。必ずスラッシュに置き換えてください
//あとリストウィンドウが更新されなくなるバグがあるようです。原因不明。症状が起きたら一旦ウィンドウを消してみてください(放置でも動作に支障なし)

string $DBdir01 = "C:/onogu2/MAYAproject";
string $DBname01 = "C/ローカルtempフォルダ1";

string $DBdir02 = "C:/onogu2/MAYAproject";
string $DBname02 = "C/ローカルtempフォルダ2";

string $DBdir03 = "C:/onogu2/MAYAproject";
string $DBname03 = "C/ローカルtempフォルダ3";

string $DBdir04 = "C:/users";
string $DBname04 ="  (未登録)  ";

string $DBdir05 = "C:/users";
string $DBname05 = "  (未登録)  ";

string $DBdir06 = "C:/users";
string $DBname06 = "  (未登録)  ";

///////////////////////////////////////////////////////////////////////////

if(`window -ex prjWindow`) deleteUI prjWindow;

//DBリスト1
proc changeDB01()
{
global string $DBdir01;
setProject $DBdir01;
    //ディレクトリリスト更新
    if(`window -ex listWindow`) deleteUI listWindow;
    window -wh 250 120 listWindow;
    paneLayout ProjPanes;
    projFileViewer -dm 0 ProjFileView;
    paneLayout -e -cn single -sp ProjFileView 1 ProjPanes;
    projFileViewer -e -fr ProjFileView;
    showWindow;
}

//DBリスト2
proc changeDB02()
{
global string $DBdir02;
setProject $DBdir02;
    //ディレクトリリスト更新
    if(`window -ex listWindow`) deleteUI listWindow;
    window -rtf 0 -wh 250 120 listWindow;
    paneLayout ProjPanes;
    projFileViewer -dm 0 ProjFileView;
    paneLayout -e -cn single -sp ProjFileView 1 ProjPanes;
    projFileViewer -e -fr ProjFileView;
    showWindow;
}

//DBリスト3
proc changeDB03()
{
global string $DBdir03;
setProject $DBdir03;
    //ディレクトリリスト更新
    if(`window -ex listWindow`) deleteUI listWindow;
    window -rtf 0 -wh 250 120 listWindow;
    paneLayout ProjPanes;
    projFileViewer -dm 0 ProjFileView;
    paneLayout -e -cn single -sp ProjFileView 1 ProjPanes;
    projFileViewer -e -fr ProjFileView;
    showWindow;
}

//DBリスト4
proc changeDB04()
{
global string $DBdir04;
setProject $DBdir04;
    //ディレクトリリスト更新
    if(`window -ex listWindow`) deleteUI listWindow;
    window -rtf 0 -wh 250 120 listWindow;
    paneLayout ProjPanes;
    projFileViewer -dm 0 ProjFileView;
    paneLayout -e -cn single -sp ProjFileView 1 ProjPanes;
    projFileViewer -e -fr ProjFileView;
    showWindow;
}

//DBリスト5
proc changeDB05()
{
global string $DBdir05;
setProject $DBdir05;
    //ディレクトリリスト更新
    if(`window -ex listWindow`) deleteUI listWindow;
    window -rtf 0 -wh 250 120 listWindow;
    paneLayout ProjPanes;
    projFileViewer -dm 0 ProjFileView;
    paneLayout -e -cn single -sp ProjFileView 1 ProjPanes;
    projFileViewer -e -fr ProjFileView;
    showWindow;
}

//DBリスト6
proc changeDB06()
{
global string $DBdir06;
setProject $DBdir06;
    //ディレクトリリスト更新
    if(`window -ex listWindow`) deleteUI listWindow;
    window -rtf 0 -wh 250 120 listWindow;
    paneLayout ProjPanes;
    projFileViewer -dm 0 ProjFileView;
    paneLayout -e -cn single -sp ProjFileView 1 ProjPanes;
    projFileViewer -e -fr ProjFileView;
    showWindow;
}

///////////////////////////////////////////////////////////////////////////
//ファイルのロード
proc load()
{
OpenScene;
}

//ファイルのセーブ
proc save()
{
SaveSceneAs;
}

//ファイルの上書き保存
proc saveOverwrite()
{
file -save;
}

//ファイルのインポート
proc import()
{
Import;
}



///////////////////////////////////////////////////////////////////////////
//ボタンつきウィンドウの設定
window -title "プロジェクトマネージャー" prjWindow;
columnLayout -adjustableColumn true;
    text -label "プロジェクトDBの変更";
    button -label $DBname01 -align "left" -command "changeDB01";
    button -label $DBname02 -align "left" -command "changeDB02";
    button -label $DBname03 -align "left" -command "changeDB03";
    button -label $DBname04 -align "left" -command "changeDB04";
    button -label $DBname05 -align "left" -command "changeDB05";
    button -label $DBname06 -align "left" -command "changeDB06";
    separator -height 20 ;
    setParent ..;
rowLayout -numberOfColumns 4 -columnWidth4 60 60 90 60;
    button -label " ロード " -command "load";
    button -label " セーブ " -command "save";
    button -label " 上書き保存 " -command "saveOverwrite";
    button -label "インポート" -command "Import";
    setParent ..;
showWindow;
window -e -h 280 -w 320 prjWindow;
///////////////////////////////////////////////////////////////////////////





スクリプトのインデント

ブログはhtmlなのでタブによるインデントが無効になってしまう。
インデントがないと見づらいので

「&emsp;」

で代用する(&は半角に。半角だとスペースとして表示されてしまうのでとりあえず全角にしてあります)





2009年5月18日月曜日

【MEL】ファイルへの配列書き込み 2

(fopen、fprint、fwrite、fclose)

プログラマーから答えをもらっちゃったので(^^;)こっちにコピペしておく。for文で配列を変換しなくてもいいらしい。というか、配列を直接ファイルに書き込もうとするからイカン。for文中で$valにそのとき代入されているものを1つづつ追記するのが以下の構文。

------------------------------------------------

★★★★文字列配列書き込み★★★★

{
string $str[] = { "aaa", "bbb", "ccc", "ddd" };
string $fname = "C:/onogu2/test.txt";

int $fid = `fopen $fname "w"`;

string $val;

  for( $val in $str )
  {
  $val+="\n";
  fprint $fid $val;
  }

fclose $fid;
}



★★★★文字列配列読み込み★★★★

{
string $str[];
string $fname = "C:/onogu2/test.txt";

int $fid = `fopen $fname "r"`;

string $val;
int $cnt = 0;

  for( $val = `fgetline $fid` ; size($val) > 0 ; $val = `fgetline $fid` )
  {
  $str[ $cnt ] = $val;
  $cnt++;
  }

  for( $val in $str )
  {
  print $val;
  }

fclose $fid;
}





【MEL】ファイルへの配列書き込み

(fopen、fprint、fwrite、fclose)

★単なる変数ならなにも考えずにfprintで書き込めるんだけど、配列は書き込めない。その解決方法
-------------------------



//ファイル書き込み命令fprintは配列をそのまま書き込めないようなので、配列を一旦変数に置き換える。
//『$変数 = $配列』のような記述は型変換できないので、for文で配列の中身を1つずつ追加していく構文にする


string $list = ""; 
string $str[] = { "aaa", "bbb", "ccc", "ddd" };
    for( $val in $str)
    {
    $list += ($val + "\n"); 
    }
print $list; //ここはチェック用に表示してるだけ


//ここから書き込みスクリプト。
//"C:/onogu2/test2.mel"が書き込むファイルの場所と名前。ファイルは存在しなければ新規に作成される。拡張子は好きにつけられる

$exampleFileName = "C:/onogu2/test2.txt";
$fileId=`fopen $exampleFileName "w"`;
    for ($val2 in $tmp);
    {
    fprint $fileId $list;
    }
fclose $fileId;