Skip to content

Posts tagged ‘Xpresso’

XPresso 基礎 1: XPressoを作る

■講座テキスト

  1. XPresso 基礎 1: XPressoを作る
  2. XPresso 基礎 2: ループ
  3. XPresso基礎 3: 条件分岐
  4. XPresso基礎 4: XGroup

章番号 題名 内容、及び関連する章 作成日/注記
801 1_XPressoを作る XPressoを作る、XPressoを止める、COFFEEノード、エクスプレッションエディタ、コンソール、Hello World! 2011.1.29


Step 1

XPressoを作る

さてこれからXPressoについて勉強していきます。「XPresso」というのは、CINEMA 4Dに内蔵されているノードベースのプログラミング環境の名前です。とは言っても言葉で説明できるものではないので、実際にプログラムを組みながら少しずつ説明していきます。

まず新規ファイルを作成し、ヌルオブジェクトを作成して下さい。このオブジェクトはプログラムとは関係ありませんが、XPressoは「タグ」の形を取るので、オブジェクトが一つもないと作れないのです。

次にヌルオブジェクトを右クリックし、プルダウンメニューから「CINEMA 4Dタグ -> XPresso」を選択します。すると、オブジェクトの右にXPressoタグができ、同時にXPresso編集ウインドウが表示されるはずです。

図801-1

さて、それではまずプログラムの教科書でよく使われる「Hello World!」を表示させてみましょう。とは言っても、普通の方法では面白くないので、CINEMA 4Dらしい方法で表示させます。オブジェクトマネージャに戻って、「オブジェクト -> スプラインプリミティブ」から「テキスト」オブジェクトを作成して下さい。そして、テキストスプラインオブジェクトをXPresso編集ウインドウにドラッグして下さい。

図801-2

次に、テキストスプラインに文字を入力するために「定数」ノードを作成します。XPresso編集ウインドウ内で直接新しいノードを作るには、画面内の空いているところで右クリックし、プルダウンメニューから必要なノードを選択します。

定数ノードは「新規ノード -> XPresso -> 一般」の中に入っています。また、先ほどドラッグアンドドロップで作成したノードも、実はこのメニューの中に入っている「オブジェクト」ノードと同じものです。

それでは、定数ノードを選択してそのパラメータを属性マネージャに表示させます。そして、データタイプを文字に変更し、値のフィールドに「Hello World!」と入力します。

図801-3

次に、この定数ノードの値をテキストスプラインオブジェクトに伝えます。そのためにはまず、ノードの左上をクリックして「オブジェクトの属性 -> テキスト」ポートを作ります(デフォルトでは、ノードの左側が青く表示され、入力ポートを追加できます。ノードの右側は赤く表示され、出力ポートを追加できます)。

図801-4

そして、定数ノードとスプラインオブジェクトのポートの間をドラッグアンドドロップしてつなぎます。

図801-5

この状態でエディタビューを見るとテキストスプラインが「Hello World!」に変っているはずです。

図801-6

 

 

Step 2

XPressoを止める

それでは他にもいろいろなところに「Hello World!」を出してみましょう。

まずXPresso編集ウインドウ内部に出します。これはXPressoのバグチェックをする時にとても便利です。まず「新規ノード -> XPresso -> 一般」から「結果」ノードを作成して下さい。

次に属性マネージャでデータタイプを文字に変更し、定数ノードの出力ポートにつなぎます。すると結果ノードの中に「Hello World!」と表示されるはずです。

図801-7

次に、テキストスプラインオブジェクト自体の名前も変えてしまいましょう。テキストスプラインを含んだオブジェクトノードの左側に「基本属性 -> 名前」ポートを作成し、定数ノードにリンクします。この状態でオブジェクトマネージャを見ると、テキストスプラインの名前が「Text」から「Hello World!」に変っているはずです。

図801-8

もし変っていない場合は、XPresso編集ウインドウで「計算 -> XPressoを実行」コマンドを選択したり、オブジェクトマネージャの空いている部分をクリックしてみて下さい。するとXPressoが実行され、表示が更新されるはずです。また、小さなノードにポートを追加すると表示がはみ出します。このような場合は、Controlキーを押しながらノードのタイトル部分をダブルクリックすると適切にリサイズされます。

 

さて、ここでちょっと実験をしてみましょう。オブジェクトマネージャでテキストスプラインオブジェクトを選択し、名前を「Hello World!」から元の「Text」に戻すのです。

名前を入力してエンターキーを押すと、すぐに名前が「Hello World!」に戻るはずです。つまり、この名前パラメータはXPressoに支配されていて、オブジェクトマネージャからは変更できなくなっています。

これはこれで不便です。もちろんXPressoを削除すればこの働きを消せるわけですが、XPressoを消さずに一時的に働きを止める方法はないもんでしょうか。CINEMA 4Dの中にはXPressoの働きを止める方法が何段階か用意されています。

一番上にあるのが「編集 -> エクスプレッションを使う」のチェックを外す方法です。これで全てのエクスプレッションが一時停止します(XPressoやCOFFEE、プラグイン等はエクスプレッションに含まれます)。この状態でテキストスプラインに新しい名前を入力すると、その通りに変更されるはずです。

図801-9

次に、オブジェクトマネージャでXPressoタグを選択して属性マネージャを開き、そこで「使用する」のチェックを外すと、個々のXPressoの働きを一時停止できます。

図801-10

最後に、XPresso編集ウインドウでテキストオブジェクトノードを右クリックし、プルダウンメニューから「停止」をチェックすると、個々のXPressoノードの働きを一時停止できます。この操作は属性マネージャからでもできます。

図801-11

これで安心してXPressoを使えるようになりました。

 

 

Step 3

COFFEEノード

それでは、さらにまた別の方法を紹介します。それはCOFFEEノードを使う方法です。COFFEEノードを使うと、昔ながらにテキストでプログラムを書けます。一見難しそうですが、実は複雑なプログラムを作る場合は、こちらの方が遥かに見やすくて簡単です。また、実際問題としてCOFFEEノードを使わずに複雑なプログラムは作れません。

CINEMA 4DにはXPressoタグ以外にも従来からの「COFFEEタグ」があり、ここでは全てテキストでプログラムを書きます。しかし、COFFEEタグの中ではThinkingParticlesを扱えません。したがって、XPressoの中で必要に応じてCOFFEEノードを使うというのが、現在のCINEMA 4Dにおいて最も簡単で強力なプログラム法なのです。

それでは、XPresso編集ウインドウに戻って、空いている部分を右クリックし「新規ノード -> XPresso -> 計算」からCOFFEEノードを選択してください。

図801-12

COFFEEノードにはデフォルトで3個のポートが付きますが、まずこれを消しましょう。ポートを消すにはポートをダブルクリックします。ポートにワイアが付いている場合は最初のダブルクリックでまずワイアが消えます。

次にCOFFEEノードを選択した状態で属性マネージャを見ます。

図801-13

ここでまず重要なのは、「属性マネージャのフィールドでプログラムを編集してはいけない」という点です。一応属性マネージャでもプログラムの編集はできます。しかし属性マネージャの仕様ではプログラムがカラー表示されず、またタブやリターンで次のフィールドに移動するようになっていて、非常にプログラムしにくいのです。そこで、左下にある「COFFEEエディタを開く」をクリックして下さい。すると「エクスプレッションエディタ」ウインドウが開き、属性マネージャに表示されていたプログラムがその中にも表示されるはずです。

図801-14

エクスプレッションエディタは、元々COFFEEタグを編集するために作られたもので、この中では普通にプログラムを書けます。ただし、タグやノードを切り替えてもエクスプレッションエディタの中身は切り替わらないので注意して下さい。毎回属性マネージャで「COFFEEエディタを開く」ボタンを押す必要があります。ちょっと面倒ですが、いろいろな技術が歴史的に積み重なっているので仕方がありません。

それではエクスプレッションエディタでの作業に移ります。まずデフォルトで付いていた3個のポートに対するプログラムを消去して下さい。

次に以下の1行を追加します。

	println("Hello World!");

そしてエクスプレッションエディタの上にある「実行」ボタンを押すと、「コンソール」ウインドウに「Hello World!」と表示されるはずです。

図801-15

コンソールウインドウが開いていない場合は、ウインドウメニューから選択して開いて下さい。また何も表示されない場合は、前のステップでXPressoを止めたままになっていないかどうかチェックして下さい。

結果ノードと違ってコンソールウインドウは出力の履歴が残るので、プログラムのデバッグ時に重要な役割を果たします。

 

また、COFFEEの働きを止めるには、止めたい行の先頭に「//」 を追加します。たとえば、プログラムを次のように変更すると、行の色が緑に変わり、実行ボタンを押してもコンソールウインドウには何も表示されません。

	//println("Hello World!");

図801-21

止めたい部分が複数の行に渡る場合は、その部分を「/*」と「*/」で囲みます。

 

 

Step 4

COFFEEノードにポートを追加する

最後に、COFFEEノードでポートと情報をやり取りする方法を説明しましょう。まずCOFFEEノードの左上をクリックし、文字ポートを作ります。次にポート名を右クリックし、プルダウンメニューから「名前を変更」を選択し、判りやすい名前に変更します。ここでは「n_in」としておきましょう。

次に、このポートを定数ノードにつなぎます。これで、COFFEEノードは定数ノードから「Hello World!」という文字情報を受け取れるようになりました。

図801-16

次に、エクスプレッションエディタに戻り、プログラムを次のように変更し、実行ボタンを押します。すると、コンソールにもう一行「Hello World!」が増えたはずです。簡単ですね。

	println(n_in);

図801-17

次に、COFFEEノードの右側にも文字ポートを追加し、名前を「n_out」とします。そしてプログラムに次の1行を追加します。

	n_out= n_in;

そして、このn_outポートを結果ノードにつなぎます。

図801-18

すると結果ノードには前と同じように「Hello World!」が表示されたはずです。表示は「Hello World!」のまま変りませんが、この文字データはCOFFEEノードの中を通ってきたものです。ですからCOFFEEノードの中でプログラムを追加すれば自由に操作できます。

たとえばプログラムを次のように変更すれば、結果ノードの表示は「Hello World + COFFEE」に変化したはずです。ここで「stradd()」は文字を追加する関数です。

	n_out= stradd(n_in, " + COFFEE");

図801-19

また、プログラムを次のように変更すれば、結果ノードの表示は「12」に変ります。12は「Hello World!」の文字数で、「sizeof()」は文字数を調べる関数、「tostring()」は整数データを文字データに変換する関数です。

	n_out=	tostring(sizeof(n_in));

図801-20

たくさんあるCOFFEEの関数を憶えるのは大変だと思うでしょうが、憶える必要は全くありません。その度にマニュアルを調べればいいのです。私もマニュアルを調べながらこのテキストを書いていますが、世の中そんなもんです。また、マニュアルを読んでも判らないことはどんどん掲示板に質問しましょう。

COFFEEのマニュアルは以下のページから入手できます。

http://www.maxon.net/en/support/plugin-cafe.html

 

 

次の章ヘ 

XPresso 基礎 2: ループ

章番号 題名 内容、及び関連する章 作成日/注記
803 2_ループ ループ、くり返しノード、for文、ポイントノード、デフォーマ、形状の複製、ループの階層化 2011.1.24

 

Step 1

ループ

ループとは、多数の入力に対して一つの処理を繰り返すことです。CINEMA 4Dではポリゴンオブジェクトやスプラインオブジェクトのポイントを操作する場合によく使います。まず、ポリゴンAの「位置」データをポリゴンBに複製したいとします。それは簡単で、ポリゴンAの位置ポートとポリゴンBの位置ポートをワイアでつなぐだけです。

図803-1

さて、それでは次にポリゴンAの「形状」データをポリゴンBに複製したいとします。しかし「形状」というデータタイプは存在しません。したがって、ポリゴンAとポリゴンBを形状ポートでつなぐことはできません。それではどうしたらいいのでしょうか。

「形状」というのは、つまり「多数のポイントの位置データが集まった結果」です。そして位置というデータタイプは存在します。ですから、ポリゴンAに含まれる全てのポイントの位置データをポリゴンBに含まれる全てのポイントに複製すれば、原理的には形状を複製できるはずです。問題なのは、ポイントの数が何千個もあった場合、「どうやってそれを実行するか」ということです。そのような場合に、ループ(くり返し)機能を使います。

 

それではさっそく形状を複製するXPressoを作ってみましょう。まず新規シーンを作成し、「平面」オブジェクトを作成します。そのままだと細かすぎるので、分割数を10に減らし、編集可能にして下さい。

次に平面オブジェクトを複製し、横に並べ、片方の名前を「平面A」、もう片方を「平面B」として下さい。

次にXPressoタグを作成しますが、このような場合はグループ化して親のヌルに作成するのがいいでしょう。

図803-2

次に、XPresso編集の中に平面Aと平面Bをドラッグアンドドロップします。平面Aから平面Bに形状を複製するので、平面Aが左に、平面Bが右になるように少し離して並べて下さい。そして、ノードの右上をクリックし「オブジェクト」ポートを追加します。

図803-3

次に、XPresso編集の空いている部分を右クリックして、「XPresso -> 一般」から「ポイント」ノードを作成してください。ポイントノードをオブジェクトノードにリンクすると、ここからポイントのデータを取り出せるようになります。さらに、ポイントノードを複製して平面Aと平面Bのオブジェクトポートにリンクして下さい。

図803-4

次に、平面Bにリンクされたポイントノードの左上をクリックし「ポイントの位置」入力ポートを追加し、平面Aの「ポイントの位置」出力ポートにリンクして下さい。これでとりあえず、平面Aのポイント位置データを平面Bに複製できたはずです。

図803-5

この状態でエディタービューを見てみましょう。一見しておかしいですが、これから順を追って修正していきます。

図803-6

まず、平面Aの左端のポイント(ポイント番号0)を動かしてみます。すると、確かに平面Bのポイントもついてきます。つまり、形状の複製ができています。

図803-7

最初の問題は、ポイントの位置情報がローカル座標ではなく、グローバル座標で扱われていることです。もちろんグローバル座標で位置データを扱うケースもありますが、今回は「形状の複製」が目標なので、ローカル座標で扱う必要があります。そこで、ポイントノードを両方とも選択し、属性マネージャで「マトリックスモード」をグローバルからローカルに切り替えます。

この状態で平面Aのポイント0を動かすと、平面Bのポイント0が「正しく」動くはずです。

図803-8

しかし、平面Aのポイント1を動かしても平面Bのポイント1は動きません。これはポイントを複製する作業が一回しか行われていないからです。そこで、この作業がくり返し行われるようにループ機能を追加します。

図803-9

 

 

Step 2

くり返しノード

XPressoノードをループさせるには「くり返し」ノードを使います。このノードは、XPressoタグに実行命令が「1回」来ると、他のノードに対して「指定した回数」だけ実行命令を送ります。それでは、「XPresso -> くり返し」からくり返しノードを作成して下さい。くり返しノードには「くり返し開始」、「くり返し終了」、「くり返し」の3個のポートがあります。そして、くり返し開始からくり返し終了までの値が順番にくり返しポートから出力されます。たとえばくり返し開始の値が「0」でくり返し終了の値が「5」だった場合、くり返しポートは「0、1、2、3、4、5」と整数を6回出力します。そして、これが他のノードを6回働かせる「命令」として働くわけです。

図803-10

このくり返し開始とくり返し終了の値は属性マネージャで直接入力してもいいのですが、ポリゴンのポイント数が変ると、その度変更する必要があります。そこで、平面Aのポイント数を調べてその値を使うようにします。まず平面Aノードとポイントノードを選択して、複製して下さい。

図803-11

ポイントノードの右側には「ポイント数」ポートがあり、ここからポイント数が出力されます。結果ノードを追加してチェックしてみましょう。ポイント数は「121」個と表示されています。

また、情報マネージャで平面Aのポイント数を確認すると確かに「121」個になっています。ところが、構造マネージャを見ると最後のポイントの番号は「120」になっています。

図803-12

これは、ポイントの番号が1ではなく0から始まっているために起こるズレです。つまり、ポイント数の値「121」をこのままくり返し終了の値として入力すると、ポイントの複製が「122回」実行され、最後のポイント121に対する処理はエラーとなります。

そこで、面倒ですがポイントノードの後ろに「計算」ノードを追加し、121から1を引いて120にします。計算ノードは「XPresso -> 計算」の中に入っています。

図803-13

最後に、計算ノードの出力ポートをくり返しノードのくり返し終了ポートにつなぎ、くり返しポートを平面Aと平面Bにリンクされたポイントノードの「ポイント番号」ポートにつなぎます。

図803-14

これで全てのポイントの位置が複製され、「形状」を複製できたはずです。それではエディタビューで平面Aのポイントを動かしてみましょう。

図803-15

ちゃんと動きましたか。もし動かない場合は、サンプル803aを開いて自分のシーンファイルと比較してみて下さい。

今回は形状をそのまま複製しましたが、間にCOFFEEノードを挟んでいろいろな処理を追加すれば、このXPressoを応用して高度なモーフ機能を表現できます。

 

 

Step 3

デフォーマによる変形

ついでにデフォーマについても実験しておきましょう。CINEMA 4Dのデフォーマはオブジェクトを変形させますが、デフォーマの働きを切ればオブジェクトの形状は元に戻ります。つまり、CINEMA 4Dの中には「オブジェクトの元の形状」と「変形後の形状」の二つの形状データが存在するのです。それでは平面Aに「屈曲」デフォーマを追加し、適当に変形させてみましょう。しかし、平面Bは変形しません。それはポイントノードがデフォルトで「オブジェクトの元の形状」を扱うようになっているからです。

図803-16

それではXPresso編集でポイントノードを両方とも選択し、属性マネージャで「変形を考慮する」をチェックして下さい。すると、平面Bが平面Aと同様に変形するはずです。

図803-17

ここまでの作業はサンプル803bの中に入っています。

 

 

Step 4

COFFEEノードのループ

次に、COFFEEを使ってループさせる方法について説明します。COFFEEノードの中では普通にfor文を指定し、ループを実行できます。まず、for文を書いたことがない人のために、for文の構造について簡単に説明しておきます。たとえば以下のようなプログラムが書かれていた場合、「for」はループを表し、「i」はループのパラメーターを表し、「println(i);」はくり返し実行される関数を表します。そして「i=0」はくり返しが0から始まることを表し、「i<10」はiが10になったらくり返しを終了することを表し、「i++」はiが1ずつ増えることを表しています。

基本的には、前のステップで説明したくり返しノードのパラメータと同じです。

	for(i=0;i<10;i++)
	{
		println(i);
	}

それでは実験してみましょう。新規シーンを作成し、ヌルオブジェクトを作成し、XPressoタグを作成し、COFFEEノードを作成して下さい。

そして、デフォルトのポートとプログラムを全て消去し、以下のプログラムを書き込んで下さい。

	var i= 0;
	for(i=0;i<10;i++)	println(i);

図803-18

1行目は変数「i」の宣言。2行目はfor文で、コンソールへ「i」を10回くり返して出力するようになっています。結果は次のようになります。

図803-19

つまり、COFFEEノードの内部ではfor文が正しく実行されています。

 

 

Step 5

ポートへの出力は1回だけ

それでは次にポートへの出力を実験してみましょう。まずCOFFEEノードを複製します。そして、左のノードに整数出力ポートを作成し、ポート名を「i_out」に変更します。また右のノードの整数入力ポートを作成し、ポート名を「i_in」に変更し、ポートをつなぎます。

図803-20

次に、左のノードの中身を次のように変更します。

	var i= 0;
	for(i=0;i<10;i++)	i_out= i;

2行目の内容を、「コンソールへの出力」から「ポートへの出力」に変えました。

次に右のノードの中身を次のように変更します。別のCOFFEEノードを編集する際には、必ず属性マネージャの「COFFEEエディタを開く」ボタンを押して、エクスプレッションエディタの表示を切り替えて下さい。

	println(i_in);

これは単に「入力をコンソールに出力する」という意味です。

このXPressoを実行すると、コンソールには「9」しか表示されません。

図803-21

つまり、for文の中でポートへの出力をくり返し命令しても、実行されるのは最後の一つだけです。

ステップ4と5の結果をまとめると、「COFFEEノード内部ではループを実行できるが、外部のノードを含めたループはできない」ということです。この点に注意して下さい。

 

 

Step 6

全部COFFEEで作る

ステップ4と5の結果から、COFFEEでループを実行する場合は、「全ての処理をCOFFEEノード内部で完結させる必要がある」ということがわかりました。それでは、形状を複製するプログラムを全てCOFFEEノードの中で書いてみましょう。ポイントの計算まで全てCOFFEEノードの中でやってしまうと、構成は驚く程簡単になります。サンプル803cは平面Aの形状を平面Bに複製するXPressoですが、くり返しノードやポイントノードの仕事を全部COFFEEノードの中で実行しています。

図803-22

COFFEEノードの中のプログラムは多少複雑になりますが、それでも9行です。

	var o1= o1_in, o2= o2_in;
	var i;
	var pnum= o1->GetPointCount();

	for(i=0;i<pnum;i++)
	{
		var ppos= o1->GetPoint(i);
		o2->SetPoint(i,ppos);
	}

ここで、1、2行目はオブジェクトと整数を定義しています。3行目の「GetPointCount()」はポイント数を調べる関数です。

7行目の「GetPoint()」はポイントの位置を取得する関数で、8行目の「SetPoint()」はポイントの位置を指定する関数です。これらの関数は、ポイントノードの各ポートの機能に対応しています。

また、このプログラムを次のように省略して書くこともできます。

	var o1= o1_in, o2= o2_in;
	var i;

	for(i=0;i < o1->GetPointCount();i++)	o2->SetPoint(i,o1->GetPoint(i));

ただしCOFFEEにも欠点があります。COFFEEには、デフォーマで変形した形状を取り出すオプションがないのです(変形した形状を取り出すことは、不可能ではありませんが非常に大変です)。

このように、XPressoノードとCOFFEEにはそれぞれ利点と欠点があります。自分の能力や目的に合わせて適切な方法を選択するように心がけて下さい。

 

 

 

Step 7

ループの階層化

最後にループを階層化する方法について説明します。一般的に、for文は次のように階層化できます。

	var i,j;

	for(i=0;i<10;i++)
	{
		for(j=0;j<10;j++)
		{
			println(i," - ",j);
		}
	}

そして、このプログラムをCOFFEEノードに書き込んで実行すると、コンソールには次のような結果が出力されます。

図803-23

つまり、ループのパラメーターが「i」と「j」の二つになり、一つのiに対して10回のjが実行され、合計では100回の処理が実行されるわけです。

 

それでは、同じことをくり返しノードを使って実行してみましょう。まず、くり返しノードを二つ作成し、くり返し終了の値を「9」に指定します。そして、右のくり返しノードに「前のくり返し」入力ポートを作り、左のくり返しノードのくり返し出力ポートにリンクします。

図803-24

そして、COFFEEノードを作成し、デフォルトのポートを全て消去して下さい。次に、整数入力ポートを2個追加し、ポート名を「i_in」と「j_in」に変更します。さらに、デフォルトのプログラムを全て消去し、次のプログラムを書き込んで下さい。

	println(i_in," - ",j_in);

最後に、左のくり返しノードをi_inへ、右のくり返しノードをj_inへリンクします。

図803-25

すると、コンソールに次のような結果が表示されるはずです。

図803-26

一見すると正しくループされているのですが、実は「0 – 9」や「1 – 9」の値がダブって出力されていることがわかります。これではまともなプログラムは作れません。

マニュアルには「くり返しノードは階層化できる」と書いてありますが、XPressoが作られた当時から修正されずに残っている大きなバグです。将来的にも解決されないでしょう。したがって、「XPressoでループを階層化することはできない」と考えた方がいいです。

 

 

Step 8

くり返しノードの階層化

XPressoの仕様上、くり返しノードは階層化できません。つまり、ループを階層化したい場合はCOFFEEノードの中でプログラムを完結させる必要があります。しかし、CINEMA 4Dの中にはThinkingParticles等COFFEEでプログラムできない機能もあります。そのような場合には、無理にでもくり返しノードを階層化するしかありません。そこで、くり返しノードを階層化するための方法を二つ説明します。

 

一つ目はCINEMA 4Dのバグを逆手に取った方法で、全ての場合に正しく動作するかどうかは不明です。また、将来にわたって通用する保証もありません。自己責任で使って下さい。

それは非常に簡単な方法で、対象となるノードの後に「ダミーノード」を追加するのです。ダミーノードは何でも構いませんが、ここでは無難に結果ノードを使っています。

図803-27

これでコンソールには正しい結果が表示されます。対象となるノードが正しく動作する理由は、「ダブって実行されるのは最後のノードだけ」だからです。つまり、ダミーノードを後に追加することで、対象となるノードは最後のノードでなくなり、正常に動作するようになるのです。もちろんダミーノードはダブって実行されますが、プログラムの働きには影響を与えないので、問題になりません。

この方法で3重のループも正しく実行できますが、ノードを実行する順番に気を付けて下さい。ノードの順番は、XPresso編集ウインドウの左にある「XPressoマネージャ」に表示されます(ノードの順番については講習nnnで詳しく説明します)。これがくり返しノードの階層と一致し、さらに対象となるノードがくり返しノードの後にないと正しく動作しません。

図803-28

 

二つ目は、COFFEEノードの中にif文を書いて、ダブった命令を排除する方法です(if文については講習804で詳しく説明します)。論理的ではありますが、「COFFEEノードでしか使えない」とか、「最後のノードであるかどうかで動作が変る」といった欠点があります。

COFFEEノードの中に次のプログラムを書き込んで、XPressoを実行してみて下さい。コンソールに正しい結果が表示されるはずです。

var j= -1;

main()
{
	if(j_in != j)	println(i_in," - ",j_in);
	j= j_in;
}

図803-29

今回はグローバル変数を使っているので、「main()」関数を含めて表示しました。

1行目はグローバル変数「j」を定義しています。ローカル変数はXPressoノードが実行される度にリセットされますが、グローバル変数はそのまま残ります。つまり、「前に実行された時の値を記憶しておける」のです。

5行目はif文で、現在の「j_in」ポートの値と、「j」の値を比較しています。jの値というのは、つまり「前に実行された時のj_inポートの値」です。したがって、このif文の意味は「値がダブっていなければこの行を実行する」ということになります。

6行目ではj_inの値をjに入力しています。このjの値が次の実行の時にif文の中で使われます。

 

以上二つの方法を説明しましたが、「くり返しノードがダブって実行されている」という状況は何も変わっていません。一つ目の方法はバグが発生する場所を移動しただけで、二つ目の方法はバグが発生した時に対象となるノードの働きを止めているだけです。一般論として、バグの周囲には他のバグが隠れているものです。ですから、ループを階層化する場合は、可能な限りCOFFEEを使うように心がけて下さい。

前の章ヘ 次の章ヘ

XPresso基礎 3: 条件分岐

章番号 題名 内容、及び関連する章 作成日/注記
804 3_条件分岐 条件分岐、if文、switch文、条件分岐ノード、限定ノード、比較ノード、ブール演算ノード 2011.1.25


Step 1

if文

XPressoにはいわゆる「if文」を表現するノードがありません。ループの場合もそうですが、ノードをワイアでつなぐタイプのプログラムでは、「くり返し」や「条件分岐」、「ジャンプ」といった複雑な構造を表現するのが難しいのです。それでも条件分岐なしでは何も作れないので、XPressoで条件分岐を表現するための方法をいろいろと説明します。

まず、新規シーンを作成し、ヌルオブジェクトを作成し、XPressoタグを作成して下さい。今回は、このヌルオブジェクトのX方向の移動範囲を「-100から100の間に制限する」ことを考えます。

テキストベースであれば、これは以下の3行のプログラムで簡単に表現できます。

	if(f_in <= 100 && f_in >= -100)	f_out = f_in;
	else if(f_in > 100)	f_out = 100;
	else	f_out= -100;

ここで、1行目の「if()」は条件文を表し、()の中の「&&」は「かつ」を表します。つまり、この行を日本語で表現すると、「もし、f_inが100と等しいか小さく、かつ、f_inが-100と等しいか大きい場合は、f_inをそのままf_outに出力する」となります。これはつまり、「何も制限しない」ということを意味します。

2行目の「else if」は「そうでなく、もし」を表します。つまりこの行は、「そうでなく、もし、f_inが100より大きい場合は、f_outに100を出力する」となります。これはつまり、「上限値を100に制限する」ということを意味します。

3行目の「else」は「そうでない場合は」を表します。つまりこの行は、「そうでない場合は、f_outに-100を出力する」となります。これはつまり、「下限値を-100に制限する」ということを意味します。

実際このプログラムをCOFFEEノードに書き込んで、ヌルオブジェクトノードの間に挟めば、簡単に値を「-100から100の間に制限する」ことができます。

図804-1

サンプル804aを開いて、エディタビューでヌルオブジェクトを動かしてみて下さい。

図804-2

それでは同じことをXPressoで表現してみましょう。

実は値を制限するだけなら簡単です。XPressoには「限定」という専用ノードがあるからです。「XPresso -> 計算」から限定ノードを作成し、COFFEEノードの代わりにヌルオブジェクトの間にはさみ、最大値を「100」、最小値を「-100」に変更してみて下さい。

図804-3

サンプル804bを開いて、エディタビューでヌルオブジェクトを動かしてみると、先ほどのCOFFEEと同じように移動範囲が-100から100の間に制限されていることがわかるはずです。

簡単ですか。しかしこのような専用ノードが用意されていること自体、「XPressoで条件分岐を表現するのが難しい」ということの証明なのです。

 

 

Step 2

ノードを組み合わせてif文を表現する

しかし、値を制限することだけが条件分岐ではありません。もっと複雑な条件分岐を表現するには、XPressoノードを組み合わせてif文と同じ働きをする構造を作る必要があります。ただし、実用的ではないので制作過程は省略します。サンプル804cを開いて各自調べてみて下さい。

図804-4

このXPressoは、おそらくステップ1でCOFFEEを使って書いたif文と同じ働きをします。それでは対応を見ていきましょう。

一番左のノードは「f_in」に対応します。

左から2、3列目の「比較」ノードは、if文に続く条件式「f_in <= 100」、「f_in >= -100」、「f_in >100」に対応します。

4列目のブール演算ノードは「&&」と「else」に対応します。下のブール演算ノードの働きがちょっと判りにくいのですが、これは上のブール演算ノードの結果と、2列目下の比較ノードの値の両方が「偽(FALSE)」であった場合に「真(TRUE)」になります。

5列目は確認用の結果ノードです。

6列目は出力する値「f_in」、「100」、「-100」に対応します。7列目は出力先のヌルオブジェクトで、「on」ポートをオンオフすることにより、この入力を実行するかどうか決めています。

ちなみに、1行目が「制限なし」、2行目が「上限値」、3行目が「下限値」に対応しています。

これでif文を完全に再現できました。しかし、COFFEEのif文が不要な行をスキップするのに対して、XPressoでは全てのノードが必ず実行されます。つまりその分遅くなります。

 

大変ですねえ。私もこんなものは初めて作りました。プログラムの経験がない人は何をやっているのかさっぱり判らないと思いますが、気にする必要はありません。悪いのはあなたではなくXPressoの方です。つまり、「XPressoではif文を表現できない」のです。

if文は階層化できますが、XPressoで階層化されたif文を表現するのは事実上無理です。私はやったことありませんし、やってみたいとも思いません。COFFEEで書いた方がずっと簡単です。

 

 

Step 3

条件分岐ノード

XPressoにはC言語のswitch文に似た働きをする「条件分岐」ノードがあります。「どうしてもCOFFEEを書きたくない」という人はこれを使うしかありませんが、あまりおすすめはできません。条件分岐ノードには、「複数の処理を実行できない」、「処理の続行やbreakの設定ができない」、「defaultの設定ができない」といった機能上の欠点があるからです。もちろんCOFFEEノードを使えばswitch文も普通に扱えます。

それでは条件分岐ノードを使ってマテリアルを切り替える実験をしてみましょう。

新規シーンを作成し、「立方体」オブジェクトを作成し、XPressoタグを作成して下さい。そして、マテリアルマネージャでカラーの異なるマテリアルを3個作って下さい。

次に、立方体にマテリアルを適用し、テクスチャタグを作成して下さい。マテリアルはどれでも構いません。

図804-5

次に、XPresso編集ウインドウに「立方体オブジェクト」、「テクスチャタグ」、「3個のマテリアル」をドラッグします。

図804-6

それではこれから、「オブジェクトの高さ(Y方向の位置)に応じて色を変えるXPresso」を作ります。

まず、「XPresso -> 論理」から「条件分岐」ノードを作成します。また、「XPresso -> 計算」から計算ノードを作成します。これで必要なノードは全てそろいました。

図804-7

まず、立方体ノードに「位置.Y」出力ポートを作成し、計算ノードの入力につなぎます。計算ノードのデータタイプは「整数」、演算タイプは「除算」、入力2は「100」に変更して下さい。これによって、立方体の高さが100変った時に、出力が1変るように値がスケールされます。

また同時に、位置データが浮動小数から整数に変換されます。

図804-8

次に、計算ノードの出力を条件分岐ノードの「スイッチ」ポートにつないで下さい。条件分岐ノードは、スイッチポートの値が0の時に一番上の入力ポートにリンクされたデータを出力ポートに出力します。そして、スイッチポートの値が1の時に2番目の入力ポートのデータを出力します。入力ポートが多数ある場合は、順番にスイッチポートの値に対応したデータが出力されます。

入力ポートはいくらでも増やせます。また、スイッチポートの値がマイナスの場合、一番上の入力ポートのデータが出力されます。また、スイッチポートの値が入力ポートの総数を超えた場合、一番上の入力ポートに戻ります。

次に、マテリアルがリンクされた3個のオブジェクトノードに「オブジェクト」出力ポートを作成し、条件分岐ノードにつなぎます。条件分岐ノードのデータタイプは「リンク」に変更します。さらに、テクスチャタグノードに「タグの属性 -> マテリアル」入力ポートを作成し、条件分岐ノードの出力ポートにつなぎます。すると次のようになるはずです。

図804-9

サンプル804dを開いて、立方体オブジェクトを上下に動かしてみて下さい。位置によって立方体に適用されるマテリアルが切り替わり、色が変わるはずです。

図804-10

 

 

Step 4

COFFEEでswitch文を使う

それでは比較のために、COFFEEノードを使って同じXPressoを作ってみます。この場合、XPressoの構成は次のようになり。条件分岐ノードを使った場合と大差ありません。サンプル804eを開いて確認してみて下さい。

図804-11

また、COFFEEノードの中のプログラムは次のようになります。

	switch(int(r_in/100))
	{
		case 0:l_out= l1_in;	break;
		case 1:l_out= l2_in;	break;
		case 2:l_out= l3_in;	break;
	}

ここで、1行目の「switch()」はswitch文であることを表します。switch文の()の中には整数を入力する必要があり、これが実行する「行数」を表します。しかし、立方体の高さの値は連続的に変化する浮動小数(例、1.234)であるため、「int()」関数を使って整数(例、1)にキャスト(変換)しています。

3行目の「case 0:」は0行目を表し、switchの値が0の時にこの行が実行されます。また、この行が実行された後、下の行も順番に全て実行されます。もしこの行だけで実行を停止したい場合には、行の最後に「break」を追加して止めます。

4、5行目も同じです。

switchの値に対応するcaseが存在しない場合、出力ポートのデータは変更されません。つまり、最後に出力されたデータがそのまま出力されます。このような場合に特別な処理をさせるには、「default:」という行を追加します。

 

このXPressoに限れば、条件分岐ノードを使う方がCOFFEEノードを使うより簡単です。しかし、条件分岐ノードではこれ以上複雑な機能を表現できません。その点、COFFEEノードを使えばより複雑な機能を表現できます。ですから、なるべくCOFFEEノードを使うことをお勧めします。

 

 

Step 5

全部COFFEEで書く

参考までに、全部をCOFFEEノード内部で処理する方法についてサンプル804fで説明します。これはCINEMA 4D R8でXPressoが導入されるまで、私たちがCOFFEEタグの中で普通にやっていたことです。

XPressoの構成は確かに簡単になります。

また、直接COFFEEタグの中に書き込むこともできます。もはやポートやワイアを作る必要は全くありません(サンプル804g)。

図804-12

しかし、COFFEEはずいぶん複雑になります。

図804-13

	var doc= GetActiveDocument();

	var o1= doc->GetFirstObject();

	var t1= o1->GetFirstTag();	t1= t1->GetNext();	t1= t1->GetNext();
	var m0m= t1->GetMaterial();
	var pos= o1->GetPosition();

	var m1= doc->GetFirstMaterial();	var m1m= m1->GetMarker();
	var m2= m1->GetNext();	var m2m= m2->GetMarker();
	var m3= m2->GetNext();	var m3m= m3->GetMarker();

	switch(int(pos.y/100))
	{
		case 0:m0m= m1m;	break;
		case 1:m0m= m2m;	break;
		case 2:m0m= m3m;	break;
	}

	t1->SetMaterial(m0m);

ここで、1行目は現在のシーンファイルを取得しています(COFFEEタグの場合は自動的に取得されます)。

3行目は、立方体オブジェクトを取得しています。ここでは、「オブジェクトマネージャの一番上にあるオブジェクト」として取得していますが、オブジェクトが増えた場合は名前や階層にしたがってオブジェクトを検索するプログラムを追加する必要があります(COFFEEタグの場合は自動的に取得されます)。

5行目は、テクスチャタグを取得しています。ここでは「一番左にあるタグ、の右にあるタグ、の右にあるタグ」として取得していますが、タグが増えた場合は名前や順番にしたがってタグを検索するプログラムを追加する必要があります。

6行目は、テクスチャタグにリンクされているマテリアルのマーカーを取得しています。

7行目は、立方体の位置ベクトルを取得しています。

9行目は、マテリアルマネージャの一番左(上)にあるマテリアルを取得し、さらにそのマーカーを取得しています。

10行目は、一つ右(下)にあるマテリアルと、そのマーカーを取得しています。

11行目は、さらに一つ右(下)にあるマテリアル、とそのマーカーを取得しています。

13から18行目までは、変数名は変ってますが、ステップ4で説明したswitch文と全く同じです。ちなみに、13行目の「pos.y」は、「posベクトルのy成分」という意味です。

20行目は、switch文の中で選択されたマテリアルのマーカーを、テクスチャタグに設定しています。

 

ずいぶん面倒ですね。私もこのようなプログラムを書いたのは数年ぶりです。なぜなら、XPressoが導入されて以降、オブジェクトを取得するのにCOFFEEを使うことはほとんどなくなったからです。ここまでのステップを振り返ってみると、XPressoとCOFFEEの得手不得手がよくわかります。

1. XPressoは、オブジェクトやマテリアルを取得するのは得意だが、ループや条件分岐を処理するのが苦手。

2. COFFEEは、ループや条件分岐を処理するのは得意だが、オブジェクトやマテリアルを取得するのが苦手。

つまり、XPressoとCOFFEEをうまく組み合わせて使うのが一番いいのです。今回の実験の中ではステップ4がそれに相当します。

 

前の章ヘ 次の章ヘ

XPresso基礎 4: XGroup

章番号 題名 内容、及び関連する章 作成日/注記
806 4_XGroup XGroup、Xプール、ターゲットXPresso、グローバル座標とローカル座標(絶対座標と相対座標)、座標変換を固定 2011.1.20


Step 1

ターゲットXPresso

これからXPressoノードをグループ化する方法について説明します。とは言っても、何もないとグループ化できないので、最初にちょっと複雑なものを作りましょう。何か役に立つもの、後で再利用できるものがいいので、ここでは「ターゲットの方を向くXPresso」を作ります。もちろんCINEMA 4Dには標準でターゲットエクスプレッションがあります。しかし、これをXPressoの中に組み込むことはできません。その点XPressoでターゲット機能を作っておけば、XPressoの中に自由に組み込むことができます。

ターゲット機能を作るには、大学で学ぶ数学の知識が一つ必要になります。それは「ベクトルA、Bの外積ベクトルCは、ベクトルA、Bに(反時計回りに)直交する」ということです。これを使って直交する座標軸X、Y、Zを計算します。

また、マトリックス(行列)というデータタイプを扱います。CINEMA 4Dで扱う4行4列のマトリックスは、簡単に言うとオブジェクトの「位置」、「X軸」、「Y軸」、「Z軸」の4個のベクトルをまとめたものです。また、座標マネージャに表示される「位置」、「スケール」、「角度」の3個のベクトルをマトリックスから取り出すこともできます。

それでは、サンプル806aを開いて「Target」オブジェクトを動かしてみて下さい。XPressoが働いて「Root」オブジェクトがその方向を向くはずです。

図806-1

それでは、XPressoを調べてみましょう。まず、目標となるオブジェクトはXPressoタグに作ったユーザデータで指定しています。

図806-2

XPressoの構成はちょっと複雑で次のようになっています。それではこれから順番に説明していきます。

図806-3

まず左から1列目のXPressoタグノードは、ユーザーデータの値(目標となるオブジェクトへのリンク)を出力します。

2列目のオブジェクトノードは、RootとTargetの二つのオブジェクトの位置ベクトルを出力します。

3列目の計算ノードは、Targetの位置ベクトルからRootの位置ベクトルを引いて、Target方向のベクトルを出力します。

図806-4

これがZ軸になるのですが、そのままではTargetの位置によってZ軸の長さ(つまりオブジェクトのスケール)まで変ってしまうので、6列目の万能ノードを使って正規化し、単位ベクトルにします。

図806-5

4列目は外積ノードです。これに最初のZ軸ベクトルを入力し、天頂方向のベクトル(0,1,0)との外積を求めると、それはZ軸と天頂ベクトルの両方に垂直なベクトルになります(同時に水平ベクトルでもある)。これを正規化するとX軸になります。

図806-6

5列目も外積ノードです。これにZ軸ベクトルとX軸ベクトルを入力し、外積を求め、正規化するとY軸になります。

図806-7

7列目は4個のベクトルを1個のマトリックスにまとめるノードです。マトリックスにまとめることで後の処理がずっと簡単になります。8列目では、計算したマトリックスをRootオブジェクトに入力しています。

 

 

Step 2

XPressoのグループ化

それでは、今作ったターゲットXPressoをグループ化します。まず、XPresso編集の空いている部分を右クリックし、「空のXGroup」を選択して下さい。すると「XGroup」という名前の小さなノードが表示されます。 図806-8

次に、XGroup以外の全てのノードを選択し、「カット」して下さい。そして、XGroupを選択し、「ペースト」します。すると、カットしたノードが全てXGroupの中に入ります。ここで、controlキーを押しながらXGroupのタイトルバーをダブルクリックすると、内部が全部見えるように適切にリサイズできます。

図806-9

これでXPressoをグループ化できました。複雑なXPressoを見やすくする目的であればこれで十分です。XGroupの名前や色を変更すればさらに判りやすくなります。

図806-10

また、XGroupのタイトルバーを右クリックして「表示 -> 標準」を選択すれば、内部が非表示になります。

図806-11

 

 

Step 3

XGroupにポートを追加する

しかし、現在のXGroupにはポートがありません。サブルーチンで言えば「引数」のない状態です。これでは他のノードと組み合わせられません。それでは、これからXGroupにポートを追加します。XGroupにポートを追加するには構成を少し変える必要があるので、まずXGroupのタイトルバーを右クリックして「XGroupを開く」を選択して下さい。これで元に戻ります。

次に、このXPressoを「入出力部分」と「処理部分」に明確に分けます。サンプル806bを開いて下さい。

図806-12

変更点は、Rootオブジェクト(つまり、XPressoタグが適用されたオブジェクトであり、ターゲットの方向を向くオブジェクト)に「オブジェクト」ポートを追加したことです。最初のXPressoではこれが「オブジェクトへの直接リンク」になっていたので、XPresso内部では変更できませんでした。

そこで、Rootオブジェクトへのリンクを、オブジェクトポートを通して他のノードから間接的にもらうように変更したのです。こうしておけば、後でリンク先を自由に変更できます。

左から2列目のCOFFEEノードには次のようなプログラムが書かれています。

	o_out= o_in->GetObject();

ここで、「GetObject()」はタグからそれが適用されているオブジェクトを取得する関数です。これはXPressoノードでは作れません。もしCOFFEEを使いたくない場合は、Targetオブジェクトを指定した時と同じように、Rootを指定するためのユーザデータを追加し、そこから取得して下さい。

さあ、これで入出力部分と処理部分を分離できました。左の2個のノードが入出力部分で、それ以外が処理部分です。それでは、処理部分のノードを選択し、どれか一つのノードのタイトル部分を右クリックし、「XGroupに変換」を選択して下さい。すると、処理部分だけがXGroupに変換されます。

図806-13

なお、COFFEEノードから出ているワイアが二股に分かれてしまいますが、これは同じものなので、一つのポートにまとめて下さい。

図806-14

さらに、XGroupのタイトルバーを選択して「表示 -> 標準」を選択すると、複雑な内部が隠れて非常にシンプルになります。

図806-15

しかし、内部を非表示にすると何のXGroupだかわからなくなってしまうので、XGroupとポートに適切な名前を付けておきましょう。

図806-16

このように入出力用のポートを明確にしておくと、XGroupを一種の「サブルーチン」として他のプログラムと組み合わせて使えるようになります。ただし、普通のプログラムのように「一つのサブルーチンを何回も使い回す」という使い方はできません。必要な数だけXGroupを複製する必要があります。しかし、それでも毎回全てのXPressoを組み直すよりはずっと簡単です。

 

 

Step 4

Xプール

さて、XGroupを再利用可能な形にまとめたら、これを保存しておいて、将来XPressoを作る時にも役立てたいものです。もちろん普通にCINEMA 4Dのシーンファイルとして保存し、コピーペーストしてもいいのですが、XPressoにはノードやXGroupをライブラリ化する機能があります。それがXプールです。XプールはXPresso編集ウインドウの左側にあります。 図806-17

Xプールには、初めから「System Operators」と「System Preset」があります。「System Operators」は、XPressoの基本ノードで、XPresso編集ウインドウを右クリックすると表示されるプルダウンメニューの内容と同じです。「System Preset」は、MAXONが作成した応用ノードで、応用的な機能を持ったCOFFEEノードや、基本ノードを組み合わせたXGroupが入っています。

自分が作ったCOFFEEノードをXプールに登録するには、まず「編集 -> プールを作成」を選択し、プール(ライブラリ)の名前と保存する場所を指定する必要があります。するとXプールに新しいプールが表示されるので、ここに「Target XPresso」をドラッグアンドドロップして下さい。リストに「Target XPresso」XGroupが追加されたはずです。

次に、このライブラリからXGroupを取り出す実験をしてみましょう。

新規シーンを作成し、ヌルオブジェクトを作成し、XPressoタグを作成して下さい。そして、XプールからXPresso編集ウインドウに「Target XPresso」XGroupをドラッグアンドドロップします。

図806-18

すると、「Target XPresso」XGroupができたはずです。このXGroupのポートに二つのオブジェクトをリンクすれば、それはターゲットオブジェクトとターゲットの方を向くオブジェクトになります。各自確認してみて下さい。

 

 

Step 5

グローバル座標とローカル座標(絶対座標と相対座標)

さて、ステップ1で作ったTargetXPressoは実は不完全なので、ここで完成させておきます。ステップ1のTargetXPressoは、グローバル(絶対)座標では問題なく動作するのですが、ローカル(相対)座標に移すと動作しないのです。ローカル座標に移すというのは、つまり階層に入れる、子オブジェクトにする、ということです。

それではサンプル806cを開いて下さい。806cではRootオブジェクトとTargetオブジェクトがヌルオブジェクトの子オブジェクトになっています。

図806-19

さて、806cはまだ正常に動作します。それは「null_r」と「null_t」がグローバル座標の原点にあり、角度もグローバル座標に一致しているからです。言い換えると、グローバル座標とローカル座標が一致しているので、どちらで計算しても結果が変らない、ということです。

しかし、この状態でnull_tを動かしてもRootの向きは変わりません。このXPressoは破綻しました。理由は、null_tを動かしても、targetの位置の値(ローカルの)が変化しないからです。

それでは少し修正してみます。XPressoノードには「位置」の他に「絶対位置」というデータタイプがあります。これはグローバル座標における位置の値を出力するので、オブジェクトの階層に影響されません。そこで、RootとTargetオブジェクトの位置ポートを絶対位置ポートに変更してみます。

図806-20

これで、null_tを動かしても正しく動作するようになりました。一歩前進です。しかし、null_rを動かすとまた破綻します。理由は、右端にあるRootオブジェクトの入力ポートが「ローカルマトリックス」になっているからです。これは「位置」と同じで、ローカル座標での値を扱うポートです。

しかし、計算の元になる入力を「位置」から「絶対位置」に変えた段階でマトリックスの計算はローカル座標からグローバル座標に移りました。だから、その結果を入力するポートもグローバルマトリックスに変更すべきなのです。それではRootの入力ポートを「グローバルマトリックス」に変更してみましょう。

図806-21

これで、どのオブジェクトを動かしても、また回転させても、Rootの向きがTargetの方向を向くようになったはずです。

 

 

Step 6

まとめ

このXPressoは最初ローカル座標で値を計算していました。しかし、オブジェクトを階層化することで問題が生じたので、計算をグローバル座標に移したのです。ただし、何でもかんでもグローバル座標に移せばいいというものではありません。たとえば、テキスト803のステップ1を思い出して下さい。ポリゴンの形状を複製する際には、ポイントの位置をローカル座標で扱う必要がありました。

値をグローバル座標で扱うかローカル座標で扱うかは、場合によって変ります。ここが難しいところです。

また、グローバル座標(絶対座標、全体座標)はワールド座標とも呼ばれ、ローカル座標(相対座標、局所座標)はオブジェクト座標とも呼ばれます。CINEMA 4Dの中にはいくつかの用語が混在していますが、本質的には同じ概念です。またこれらを英語で表記すると、次のようになります。

絶対、Global、Absolute、World

相対、Local、Relative、Object

最後に、現在のCINEMA 4D R12(12.032)には設計上の大きな欠陥があります。

それは「座標変換を固定」機能で、これを使うとオブジェクトの位置を扱うXPressoは全て正常に動作しなくなります。これはバグではなく、設計上の欠陥なので修正できません。

また、「座標変換を固定」機能は、必要な情報をオブジェクトマネージャや属性マネージャ、座標マネージャに表示しないため、管理が非常に面倒で、バグの原因になります。このような理由から、座標変換を固定機能は「絶対に使わない」ようにして下さい。

絶対座標と相対座標、及び「座標変換を固定」機能の問題に関して

 

 

Step 7

COFFEEで作る

最後に、今回作成したXGroupの機能を1個のCOFFEEノードで表現してみます。COFFEEを使えば、そもそもXPressoノードを複雑に組み合わせる必要も、XGroupにまとめる必要もないのです。それではサンプル806dを開いて下さい。 図806-22

COFFEEの中身は次のようになっていて、XPressoノードを使った計算にほぼ対応しています。

図806-23

	var ot= ot_in;
	var or= or_in->GetObject();

	var otp= ot->GetMg()->GetV0();
	var orp= or->GetMg()->GetV0();

	var vz= vnorm(otp-orp);
	var vx= vnorm(vcross(vector(0,1,0),vz));
	var vy= vnorm(vcross(vz,vx));

	var ormg= or->GetMg();

	ormg->SetV0(orp);	ormg->SetV1(vx);	ormg->SetV2(vy);	ormg->SetV3(vz);

	or->SetMg(ormg);

ここで、1、2行目はTargetオブジェクトとRootオブジェクトを取得しています。

4、5行目は、オブジェクトから絶対位置を取り出しています。「GetMg()」はオブジェクトからグローバルマトリックスを取り出す関数、「GetV0()」はグローバルマトリックスから位置を取り出す関数です。

7〜9行目はRootオブジェクトのX、Y、Z軸を求める計算です。「vnorm()」はベクトルを正規化する関数、「vcross()」はベクトルの外積を求める関数、「vector()」はベクトルを定義する関数です。

11行目はRootからグローバルマトリックスを取り出しています。この行はマトリックスという「器」を用意するためにあり、中身は何でも構いません。new()関数を使って空のマトリックスを作ってもいいし、マトリックスポートを作ってそれを使ってもいいです。

13行目では、用意したマトリックスに4個のベクトルを入力しています。「SetV0()」は位置ベクトルを入力する関数、「SetV1()」はX軸ベクトルを入力する関数、「SetV2()」はY軸ベクトルを入力する関数、「SetV3()」はZ軸ベクトルを入力する関数です。

15行目は、完成したマトリックスをRootオブジェクトのグローバルマトリックスに入力しています。

 

このように、計算の中核部分はXPressoノードで組んでも、COFFEEで書いても似たような構成になります。つまり、XPressoノードが組めるならCOFFEEでも書けるし、COFFEEが書けないならXPressoでも組めない、ということです。

これに対して、XPresso基礎 3: 条件分岐で書いたように、入出力部分や、ループ、条件分岐等についてはそれぞれに一長一短があります。

前の章ヘ