Hello world!

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!

カテゴリー: 未分類 | 1件のコメント

expressionとMel

expressionとMelは見た目が似ているので同一視してしまうことが多いのですが、実際のところexpressionはリアルタイムで値を記述しておく物と言う意識がないと思ったとおり動かなくて困ることがあります。
「そうじゃないんだよな~」と思う動きをするときは大体これが原因です。
特にif文などで制御したいattributeを参照させる場合などです。

if(pCube1.visibility==0){
 pCube1.visibility=1;
}else{
 pCube1.visibility=`rand 0 2`;
}

このようなexpressionは不安定な動きをします。
ですので、書き換える必要があります。

if文を使わずに分岐させる方法を考えなくてはなりません。

ではどうやって分岐させればよいのでしょうか?
このような場合利用してみて欲しいのが「演算式」です。
演算で分岐させる方法は、四則演算だけで考えていると答えが見つかりにくいのですが、実のところif文もアセンブラーではただ単なる演算でしかありません。
なので、アセンブラーの手法を利用すればif文を使わずに分岐させることができるということです。

利用する演算子は「== , != , < , > ,&&, ||」などの比較演算子と呼ばれる演算子です。
if文の()のなかで利用していたものですね。
これらの演算子は演算結果として、0か1を返してくる性質がありますので、それを利用します。
たとえば上記のexpressionは以下のように書き換えることができます。

pCube1.visibility=(pCube1.visibility!=0)*(int)`rand 0 2`;

このように比較演算子で得られた値(0or1)を入力したい数字に乗算することで分岐させることができます。
これにより、この式を評価している間にステータスが変わってしまうことがなくなります。

カテゴリー: ちょいテク系 | コメントをどうぞ

ポリゴンフェイスのフラット化

選択したフェイスを一定の方向にフラット化させるscriptを作ってみました。
faceと法線方向を決めるedgeを選択し実行すると、edgeが直行する平面上に選択されたfaceの頂点座標がそろいます。

//————————————————————————————//
string $faceStr[]=`filterExpand -sm 34`;
string $edgeStr[]=`filterExpand -sm 32`;
string $list[];
string $tkBuf[];
float $tNml[]={0,0,0};
int $i=`size $faceStr`;
select $faceStr;
float $selBB[]=`xform -q -ws -bb`;
float $selCp[]={(($selBB[0]+$selBB[3])/2),(($selBB[1]+$selBB[4])/2),(($selBB[2]+$selBB[5])/2)};
float $posP[];
float $distP[];
float $t;
int $j=0;
if(`size $edgeStr`==0){
 for($work in $faceStr){
  $list=`polyInfo -faceNormals $work`;
  tokenize $list[0] " " $tkBuf;
  $tNml[0]+=((float)($tkBuf[2])/$i);
  $tNml[1]+=((float)($tkBuf[3])/$i);
  $tNml[2]+=((float)($tkBuf[4])/$i);
 }
}else{
 select $edgeStr[0];
 ConvertSelectionToVertices;
 for($work in `filterExpand -sm 31`){
   $posP=`pointPosition $work`;
  if($j==0){
   $j=1;
   $tNml[0]-=$posP[0];
   $tNml[1]-=$posP[1];
   $tNml[2]-=$posP[2];
  }else{
   $tNml[0]+=$posP[0];
   $tNml[1]+=$posP[1];
   $tNml[2]+=$posP[2];
  }
 }
}

float $nlNml[]={$tNml[0],$tNml[1],$tNml[2]};
float $d=$nlNml[0]*$selCp[0]+$nlNml[1]*$selCp[1]+$nlNml[2]*$selCp[2];
select $faceStr;
ConvertSelectionToVertices;
for($work in `filterExpand -sm 31`){
  $posP=`pointPosition $work`;
 $t=($d-($nlNml[0]*$posP[0])-($nlNml[1]*$posP[1])-($nlNml[2]*$posP[2]))/(($nlNml[0]*$nlNml[0])+($nlNml[1]*$nlNml[1])+($nlNml[2]*$nlNml[2]));
 $distP[0]=$posP[0]+($t*$nlNml[0]);
 $distP[1]=$posP[1]+($t*$nlNml[1]);
 $distP[2]=$posP[2]+($t*$nlNml[2]);
 move $distP[0] $distP[1] $distP[2] $work;
}

//————————————————————————————//

内容はとっても簡単です。
このmelで実際に頂点座標を計算している部分は以下の5行ですので・・・。

float $d=$nlNml[0]*$selCp[0]+$nlNml[1]*$selCp[1]+$nlNml[2]*$selCp[2];

 $t=($d-($nlNml[0]*$posP[0])-($nlNml[1]*$posP[1])-($nlNml[2]*$posP[2]))/(($nlNml[0]*$nlNml[0])+($nlNml[1]*$nlNml[1])+($nlNml[2]*$nlNml[2]));
 $distP[0]=$posP[0]+($t*$nlNml[0]);
 $distP[1]=$posP[1]+($t*$nlNml[1]);
 $distP[2]=$posP[2]+($t*$nlNml[2]);

縮めれば1行で済んでしまいますね・・・。($dは定数になります、ですので毎回計算しないようにループの外に出してあります)
それ以外の部分は何か?といえば、選択されたものの種別わけと、計算に必要な中心座標や法線ベクトル等を取得をしているだけです。
数学は、ほんのちょっぴり、あとは手続きの記述です。
文系&デザイナーの皆さんも恐れずに踏み込めますね。

では、melを書く上で有意義だと思われるところだけ細かく説明していきます。

■まず選択された物の種別わけをし、リストを作成します。
polygonではfilterExpandという関数をよく使用します。
この関数に引数を指定してあげると、現在選択されているものの中から、必要な種別のリストを得ることが出来ます。

string $faceStr[]=`filterExpand -sm 34`;
この行では「-sm 34」を指定しています、リファレンスを見ていただければわかるかと思いますが「34」はpolygonFaceをリスト化してくれます。

string $edgeStr[]=`filterExpand -sm 32`;
この行では「-sm 32」を指定しています、「32」はpolygonEdgeがリスト化されます。

「31」はpolygonVertices、「33」はUVです、そのほかにも色々ありますので、何がリスト化できるくらいは大雑把に覚えておくと便利です。

 

■選択された物の中心位置を求めます。

float $selBB[]=`xform -q -ws -bb`;
この行はxformで選択された物のバウンディングボックスのサイズをゲットしています。
xformは複数オブジェクトに対応していませんので扱いに注意が必要です。

float $selCp[]={(($selBB[0]+$selBB[3])/2),(($selBB[1]+$selBB[4])/2),(($selBB[2]+$selBB[5])/2)};
取得したxyzの最大値と最小値から中心点を求めます。
足して2で割れば平均値がでて、それが中心・・・という非常に単純な・・・。

■選択されていた物の数によって分岐させます。

string $edgeStr[]=`filterExpand -sm 32`;
この行によって得られたedgeのリストの個数を調べると、edgeが選択されて「いたのか、いないのか」ということがわかります。
リストの個数を調べるのはsizeという関数を使います。
size $edgeStr;
こう書くとedgeのリストを収めた配列の個数がわかります。
それをif文で分岐させます。

if(`size $edgeStr`==0){
この場合edgeStrが0か否かをチェックして分岐させています。

 

■edgeが選択されていなかったら、選択されたfaceの法線を求める。
数学では内積を使うのでしょうが、melなので関数で求めちゃいます。

$list=`polyInfo -faceNormals $work`;
polyInfoという関数にfaceのリストのメンバーを渡してそのメンバーの法線を取得しています。
ただし、face単位のstring配列で戻ってきます。
法泉はテキストの中に埋まっている状態です、なので分解しないといけません。

tokenize $list[0] " " $tkBuf;
stringを分解するにはtokenizeという関数を使います。
一番目の引数に分解したい文字列、二番目の引数に分解するためのきっかけとなる文字群の文字列、3番目に分解した後のデータが格納されるstring配列を指定します。
この場合、2番目の引数に" "(スペース)が指定されていますので、スペースで区切られた範囲をstring配列に格納してくれます。
しかし、この戻り値の中身はあくまで文字列ですので数値として利用するにはもう一手順必要です。
C言語ですとatoiやsprintfやscanfといった関数で変換しますが、Mayaはそこら辺は良い感じで自動変換しちゃってくれます。
ただし、演算式内などでは型変換をしてあげないといけません。

$tNml[0]+=((float)($tkBuf[2])/$i);
$tNml[1]+=((float)($tkBuf[3])/$i);
$tNml[2]+=((float)($tkBuf[4])/$i);

上記のように「 (float) 」と頭につけると型変換されます。(プログラム的にはキャストって言います)
でもって得られた法線を平均化するためにfaceの数で割って加算していっています。
演算式がいわゆる四則演算の表記と違っているので説明しときます。

$tNml[0]+=((float)($tkBuf[2])/$i);
この行ですが普通に書くとこうなります。
$tNml[0]=$tNml[0]+((float)($tkBuf[2])/$i);

同じ変数が同じ行の中にあると見難くなるので、演算式を工夫して見やすく、且つ何をしているのかわかりやすくしているというわけです。

 

■edgeが選択されていたら法線を求める。
前述の部分で分岐させていますので、edgeが選択されていた場合の処理のパートにedgeから法線を求めるscriptを書きます。
法線の求め方は簡単ですね・・・終点の座標から始点の座標を引き算するだけです。
今回はベクトルの方向はどちらでもかまわないのですごく楽です。

 select $edgeStr[0];
edgeを選択して・・・

 ConvertSelectionToVertices;
verticesに選択しなおします
 for($work in `filterExpand -sm 31`){
選択されたvertices分だけループするようにします。その際$workにverticesの名前を代入させてます。
   $posP=`pointPosition $work`;
頂点のワールド座標を求めています。

  if($j==0){
   $j=1;
   $tNml[0]-=$posP[0];
   $tNml[1]-=$posP[1];
   $tNml[2]-=$posP[2];
  }else{
   $tNml[0]+=$posP[0];
   $tNml[1]+=$posP[1];
   $tNml[2]+=$posP[2];
  }
 }
始点を最初に引いておいて、後から終点の座標を足してます、順番は特に意味はないです、今回は。

■演算部分
比較したい法線ベクトルが直行する平面に対して、編集したい頂点を通過点とした同じ法線ベクトルが交差する場合の直線式に代入する係数を得ます。

float $d=$nlNml[0]*$selCp[0]+$nlNml[1]*$selCp[1]+$nlNml[2]*$selCp[2];

 $t=($d-($nlNml[0]*$posP[0])-($nlNml[1]*$posP[1])-($nlNml[2]*$posP[2]))/(($nlNml[0]*$nlNml[0])+($nlNml[1]*$nlNml[1])+($nlNml[2]*$nlNml[2]));

この辺は数学の本のほうが詳しいかも。

平面の式 ax+by+cz=d
面法線ベクトル vector <<a,b,c>>

通過点を(x1,y1,z1)とした場合の直線の式
x=x1+t*a1
y=y1+t*b1
z=z1+t*c1
直線のベクトル vector <<a1,b1,c1>>

平面の式に代入して定数dを求める
a(x1+t*a1)+b(y1+t*b1)+c(z1+t*c1)=d

平面とvector<<a,b,c>>との係数を求める。
t=(d-ax1-by1-cz1)/(a+a1+b*b1+c*c1)

 $distP[0]=$posP[0]+($t*$nlNml[0]);
 $distP[1]=$posP[1]+($t*$nlNml[1]);
 $distP[2]=$posP[2]+($t*$nlNml[2]);

求められた係数を法線ベクトルにかけて移動値を得ます、それを元の位置に加算すると、平面(ax+by+cz=d)上に投射された頂点座標が得られます。
今回は平面のベクトルと投射するベクトルが同じなので同じ数値を利用してます。

 move $distP[0] $distP[1] $distP[2] $work;

んで、移動させます、以上。

カテゴリー: Modeling関係 | 3件のコメント

選択した物になにかしたい

「今、自分が何を選択しているのか知りたい」

こんな風に単体で書くと、青春の一コマのような感じですね。
でっかい声で遠い雲に聞いてみたりするもんです(・・・誰もわからないって・・オマケに近頃は捕まりま~す)

自分が何を選択しているのかをMelで知りたい場合は「ls」というコマンドに「-sl」というオプションをつけます。(たまに-lsと書いてしまうこともあります)
何かを選択している状態でcommandLineに「ls -sl」と打って実行してみましょう。
resultが返ってきていますね?

ちゃらららっちゃっちゃ~ん「○○はlsを習得した、選択したものがわかるようになりました」

「○○はlsを唱えた」
「こんぼう なべのフタ たびびとのふく」

う~ん、この呪文を使っても何も効果なし・・・と。

「選択しているものがわかってもどうしようもない」ということが判ります。
選択しているものが判って更に何かを行うことで「選択的に何かの処理を行える」と・・・。

で「何か」をするにはどうしたら良いんでしょう。

いつもオペレートしている内容をさせればいいのです。
たとえばscaleだとかmoveだとかrotateです。
でも、選択しているすべてのモデルを動かすだけなら手でも出来ます。
じゃ~一つずつ値を変えていくとかならどうでしょう?
10個とかなら手でもいいですが、100個、1000個、10000個はどうですか?
・・・これだけの量だと諦めが付きますね、さぁ!MelScriptでやりましょう!

さっき「ls -sl」を覚えたので、これも唱えてみましょう。
でもってMaya特有のループ命令を加えて一気に片付ける方法を作っちゃいましょう。
ただし、同じことをすべてのオブジェクトにしてしまっては意味が無いので、乱数を加えて見ます。

例1
for($work in `ls -sl`){
 scale 1 `rand 1 2` 1 $work;
}

選択されていたオブジェクトのYスケールがランダムになったかと思います。

では細かい説明です。

for($work in `ls -sl`){
この行は複数の命令が圧縮されています。
この命令は$workという変数に、選択されていたオブジェクトの名前を順番に代入して、終わりになったらループを終了させるという内容です。
めんどくさいので「選択されたものを順繰りに$workに入れてくれるのね」と覚えてしまいましょう~とりあえずわ。

scale 1 `rand 1 2` 1 $work;
乱数を加える命令がscaleと組み合わされていますが、それ以外はいつもやっている作業と同じですね。

これだけのことなのですが、これによってありとあらゆることができるようになります。
scale 1 `rand 1 2` 1 $work;
ここの部分の後にどんどん付け足していけば、自分のMelScriptが出来てゆきますし、「ls」の魔法の後に「"*Poly*"」と書けば「Poly」という文字列を持つオブジェクトだけ教えてくれたりします。
(オプションを書き換えるだけでいろいろなことが出来ますので色々やってみてください)

また、この魔法に組み合わせて使うと有効な「setAttr」という関数があります。
様々なアトリビュートを書き換える関数です。

たとえばさっきのscriptは一部をsetAttrで書き換えられます。

例2
for($work in `ls -sl`){
 setAttr ($work+".scaleY") `rand 1 2`;
}

同じ動きをしますね?、この魔法のいいところは一部を書き換えれば別のアトリビュートにも使えるということです、moveやらscaleやらrotate・・・たくさんの魔法はいらなくなってしまいます。

例3
for($work in `ls -sl`){
 setAttr ($work+".scaleX") `rand 1 2`;
}

yに変わってxのscaleが変わりましたね。

例4
for($work in `ls -sl`){
 setAttr ($work+".translateX") `rand 1 2`;
}

今度はx方向にmoveしました。

こうやって実験してみたら「ls」と「setAttr」を「for」で回すことで色々出来るような気になっちゃうじゃないですか。
え~実際のところいろいろ出来ます、楽しんでみてください、苦しまないでください。

苦しみそうな場合はコメントつけてくださいませ。

カテゴリー: ちょいテク系 | 1件のコメント

Melを書くためのススメ②

さて、とりあえずスライムは「にげる」でバックアタックも食らわずにやり過ごせました。
しかし、どうやら今度のイベントバトルらしいフラグをもってそうな小ボスは逃げられないようです。
全滅イベントではないようですのでリアルに戦わなければならない雰囲気がぷんぷん・・・でもファイアと薬草しかないんですが・・・。

それが次の山場である

構文です。

色々お約束がありすぎてなんともいえませんが、とりあえず頑張ってみてください。
多分、分岐(if)とかループ(for)をメインに演算子を組み合わせてどうなるのか、遊ぶつもりで触ってみてください。

例1

if(1){
 print("Kitaaaaaaa\n");
}

とか覚えたて変数を使ってもいいです。

例2
int $k=1;
if($k){
 print("Kitaaaaaaa\n");
}

例3
int $k=0;
if($k){
 print("Kitaaaaaaa\n");
}else{
 print("Konaiiiiii\n");
}

などなど。

そしてちょっとむずかしいっぽくなるループ命令

例4
int $k;
for($k=0;$k<4;$k++){
 print("Kitaaaaaaa\n");
}

>for($k=0;$k<4;$k++){
この部分を分解して説明せねばなるまい。

一言で言えばこうです。
for(初期化;評価式;インクリメントorデクリメント)

突き放された感じがしますね、ハイ、突き放したいです。

初期化=砂時計をひっくり返す瞬間です。変数の最初の値を決めてあげます。
評価式=砂時計が何分目まできたらカップヌードルが出来るといったことを設定します。
インクリメントorデクリメント=む~めんどい、落ちる砂の数を設定します。

カップヌードルの調理メソッドを題材にforを評価するとこんな感じでしょうか・・・。
for(砂時計をひっくり返す;カップヌードルが出来あがる3分のところ止める;砂ぽろぽろ落とす)

やべぇ~自分もわかんなくなった・・・。

ま~ぶっちゃけ慣れですヨ慣れ。

カテゴリー: 雑記 | コメントをどうぞ

Melを書くためのススメ①

プログラミングの知識のない方がMelを書き始めようとして最初につまづくのが、
・変数(データ型)
・構文
・配列
・関数
の順番だと思います。

さ~行くぞ手下ども、もれはScript王になる!と意気揚々とMelを書き始めて見た方が、最初にScriptで難しいと思っている部分に入る前にいきなり峠が訪れます。

変数です。

 変数に代入ってなによ? 数学のx=yみたいなもの?
「難敵の分岐命令や演算命令よりも先に現れたこやつは、きっとスライム並みの雑魚に違いない、vectorとか書いてあるのはスライムベスくらいだな、ふむふむ・・・しかしめちゃくちゃ理解できないって言うかヒットポイント高くないですか?」
といった感じで、難しいというイメージを、たかだか変数の定義の部分でいきなり醸し出してしまいます。
ある意味、お約束の慣用句でもあるのですが、はじめて触った人には「一見さんお断り」状態です。
この先に控えている者を想像するだけで腰が引けてしまいます。
例題のウサギちゃんが2羽とかリンゴが3つとか言葉ではごまかせない、何かの出現を嗅ぎ取ってしまいます。

ここを「あーよ~わ~らんけど、これでいいのね」と通り過ぎれる勇者のみ、栄冠を勝ち取れると僕は思ってます。

なので、流してください、複雑に考えずにコピペで済ませてください。

カテゴリー: 雑記 | コメントをどうぞ

Melのテキスト処理を高速化

Melを高速化する場合、特にテキストなどの出力を早くしたいときに利用すると便利なのが、下記サイトにあるsprintfです。
http://www.highend3d.com/maya/plugins/?group=mayaplugins&section=utilities

このPlug-inはC言語でよく使われるsprintf関数を制限(引数は10個以内、ソースがあるので増やせますが)がありながらもMaya上で実現させたPlug-inです。
配布されている物以外のバイナリーは無いので、場合によってはコンパイルしないと利用できませんが、使うと非常に高速化できます。

たとえば、取得した文字列と浮動小数点数を混ぜながら出力する場合を想定してみましょう。

3個の要素を持つ配列を組み合わせて出力する場合のscriptはこのようになります。

string $sampleList[]={"aaaa","bbbb","cccc"};
float $testFLList[]={1.2,1,19.444};
string $expStr;
for($i=0;$i!=3;$i++){
 $expStr+=($sampleList[$i]+" "+$testFLList[$i]+"\n");
}
print $expStr;

//result
aaaa 1.2
bbbb 1
cccc 19.444

ちゃんと出力されているので、これでもいいのです、ほとんどの場合は・・・・。

でも「ちょっと待ってください」という方だけ、ここから先を読んでください。

僕の場合の「ちょっと待ってください」は書式変更とパディングでした。
Cソースに変換する場合、データがfloatであることを主張しないといけない場合があります。
この場合「1.2」は小数点がありますから主張しています、しかし「1」はよろしくありません、「1」は「1.0」と出力したくなります。
以前は小数点以下の切捨てや、hex出力して0パディングする関数群を用意したりしていましたが、あまりにも処理が重すぎて話しになりませんでした。
それらの苦労も処理落ちも「sprintf」があれば解決です。

string $sampleList[]={"aaaa","bbbb","cccc"};
float $testFLList[]={1.2,1,19.444};
string $expStr="";
for($i=0;$i!=3;$i++){
 $expStr+=sprintf("%s %.4f\n",$sampleList[$i],$testFLList[$i]);
}
print $expStr;

//result
aaaa 1.2000
bbbb 1.0000
cccc 19.4440

浮動小数点以下が書式にしたがって4桁でそろえられています。

 

string $sampleList[]={"aaaa","bbbb","cccc"};
float $testFLList[]={1.2,1,19.444};
string $expStr="";
for($i=0;$i!=3;$i++){
 $expStr+=sprintf("%s 0x%.4x\n",$sampleList[$i],(int)$testFLList[$i]);
}
print $expStr;

//result
aaaa 0x0001
bbbb 0x0001
cccc 0x0013

hex変換してなおかつ出力も0パディングされています。

このような変換や書式変更が頻繁に行われるようなscriptの場合、最大で10倍強の高速化が行えます。

カテゴリー: Plug-in関係 | 3件のコメント