「天井から滴り落ちる落下物」のCGで、自由落下の座標計算をしてアニメーションを作成しました。Xi-TRACEでは、一度レイトレーシングを実行すると、前回の変数値などは初期化されてしまい、毎回最初からデータ記述が読み込まれる仕様ですので、自由落下の座標値を計算するのに少し苦労しました。
今回は、2015/7/31にリリースしたXi-TRACE Rel.03の新機能、ファイル入出力機能を使って、もう少しスマートに実装してみたいと思います。
前回は、データ処理の度に、毎回、フレーム番号0番から、今計算しようとしているフレーム番号n番までを再計算しなおして、最後の座標値を使っていましたので、最後の方のフレームになればなるほど、計算時間が長くかかっていました。
今回は、ファイル入出力機能を使って、最初に実行した時にすべての座標値を計算してしまい、座標値をファイルに出力しておき、2回目以降の実行時にはファイルから座標値を拾って実行する形にしてみます。
以下、地面から適切な角度で打ち上げられたボールが重力に引かれて落下、地面でバウンドするアニメーションの座標データ計算を例に見ていきます。
マニュアルの繰り返しになるため、細かくは解説しませんが、以下の手順でファイルへテキストデータを書き出すことができます。
1)fopen命令を使ってファイルを書き込みモードで開く
var fout; // ファイルディスクリプタ
fopen fout, "ファイル名", "w";
2)fputl / fputs / fputf / fprint 命令を使ってデータを1行毎に書き出し(必要回数)。
fputl fout,"行データ文字列";
fputs fout,"文字列";
等
3)fclose命令でファイルを閉じる
fclose fout;
ボールの座標計算をしながら、以下のようなファイルを作成します。
−−−−出力ファイルフォーマット
<MODELA_FID>
ball 1
<x座標1> <y座標1> <z座標1>
<x座標2> <y座標2> <z座標2>
:
<x座標n> <y座標n> <z座標n>
ball 2
<x座標1> <y座標1> <z座標1>
<x座標2> <y座標2> <z座標2>
:
<x座標n> <y座標n> <z座標n>
:
ball N
<x座標1> <y座標1> <z座標1>
<x座標2> <y座標2> <z座標2>
:
<x座標n> <y座標n> <z座標n>
−−−−ここまで
MODELA_FIDは、ファイルを実行する度にランダムに更新されるIDです。後で、アニメーションの2回目以降の実行時に座標の再計算を抑止するのに使います。
その後ろには、ボール毎に各フレームの座標値をtabで区切って出力します。次のボール座標を出力する前に「ball <番号>」というデータを出力しておきます。プログラムは以下のような感じです。
var fout;
macro lbuf;
fopen fout,$(pos_file),"w";
// MODELA_FIDを書き出し
fputl fout,#(MODELA_FID);
var idx2=0;
while (idx2<n_ball)
{
// ballインデックス
fputl fout,"ball "#(idx2);
var idx=0;
while (idx<fn_allac)
{
// 座標計算
// 座標値出力
if (idx%fn_acc=0)
{
mprint lbuf,"# # #",px,py,pz;
fputl fout,$(lbuf);
}
idx=idx + 1;
}
idx2=idx2 + 1;
}
fclose fout;
アニメーションデータ処理の最初の一回だけ、座標計算して、先ほどの座標ファイルを作成します。
そのため、実行が始まったら、最初に、座標ファイルが存在するかどうか?存在したなら、最初の1行を読み込んでみて、MODELA_FIDが同じかどうかを比較します。結果、座標ファイルが見つからなかったか、MODELA_FIDが不一致だった場合には、座標計算を実行するように動作を切り替える記述をしておきます。
// 座標ファイルのチェック
var calc_pos=true;
macro pos_file=$(dir)$/$(file)"_pos.txt";
ifacc $(pos_file)
{
// 座標ファイルが見つかった
var fin;
macro lbuf;
fopen fin,$(pos_file),"r";
fgetl fin,lbuf;
fclose fin;
// MODELA_FIDをチェック
var mfid;
str2num mfid=$(lbuf);
if (mfid=MODELA_FID)
{
calc_pos=false;
}
}
最初にcalc_posという変数をtrue(座標計算する)に設定しておき、MODELA_FIDが一致して、ファイル計算の必要がなくなった場合にfalse(座標計算済みのため、再計算しない)に設定して、次の処理につなげます。
テキストデータの読み込みも、書き出しの時とほぼ同じです。
1)fopen命令を使ってファイルを読み出しモードで開く
var fout; // ファイルディスクリプタ
fopen fin, "ファイル名", "r";
2)fgetl命令を使ってデータを1行毎に読み込み(必要回数)。
fgetl fin, <マクロ名>;
3)fclose命令でファイルを閉じる
fclose fin;
1行読み込み命令、fgetlでは、指定した区切り文字毎にフィールドに分割して読み込むことができます。ファイル出力の所に記載した通り、座標データは、1行の中にtab区切りで出力しました。MODELAはデフォルトで、tabおよびspaceを区切り文字として読み込むので、fgetl fin, <マクロ名>; を実行した段階で、配列マクロ:<マクロ名>#1に最初のフィールド(x座標)、<マクロ名>#2に2番目のフィールド(y座標)、<マクロ名>#3に3番目のフィールド(z座標)、が格納されています。これらは文字列ですので、str2num命令で数値に変換して座標値として使っています。
読み込みの記述は以下となります。
// 座標ファイルから、座標を拾う
var fin;
macro lbuf;
fopen fin,$(pos_file),"r";
fgetl fin,lbuf; // MODELA_FID
var idx2=0;
while (idx2<n_ball)
{
// ball境界まで読み飛ばし
var cmp;
repeat
{
static : fgetl fin,lbuf;
strcmp cmp=$(lbuf1),"ball";
} until (cmp=0);
// fn番目の座標データを拾う
var idx=-1;
while (idx<fn)
{
static : fgetl fin,lbuf;
idx=idx + 1;
}
str2num px#(idx2)=$(lbuf1);
str2num py#(idx2)=$(lbuf2);
str2num pz#(idx2)=$(lbuf3);
idx2=idx2 + 1;
}
fclose fin;
ファイル入出力を使った座標計算の取り扱い方法については、以上で終了しています。ただ、実際に計算してみると、地面での跳ね返りがうまく計算できていません。
自由落下する物体は、地面に到達するときに相当にスピードが上がっています。フレームレート(30[フレーム/s])毎の座標計算では粗すぎて、キレイに計算できませんでした。そこで、フレームレートのfn_acc倍の精度で座標計算させることで、許容可能な跳ね返りを計算するようにしてあります。完成したデータと動画です。
Xi-TRACEデータ:bound_ball.xitr
途中で計算を打ち切っていますが、空気抵抗が計算に入っているので、そのうち止まるはずです。地面との摩擦は計算していないので、止まるまでにはだいぶ時間がかかるはず。ボールは奥行き方向にずらしてあるので、ボール同志はぶつかりません(ボール同士の衝突も計算してません)。
ファイル入出力を使った座標計算を応用して、メタボールで噴水の水の吹き出しを表現してみようと思います。
噴水から噴出する水のある程度の塊を1つのメタボールで表現します。噴水口から適切な角度で打ち出された水の塊は、重力に引かれて放物線を描き、地面に落下します。先ほどのボールでは、地面で跳ね返る計算をしましたが、今度は、地面に落ちたところで水たまりを作るようにします。
さっきのボールは5個程度でしたが、今度は水を表現するため、もっとたくさんの水粒が必要です。
そこで、座標ファイルのフォーマットを少し改定して使います。
−−−−出力ファイルフォーマット
<MODELA_FID>
<フレーム番号> <x座標1> <y座標1> <z座標1>
<フレーム番号> <x座標2> <y座標2> <z座標2>
:
<フレーム番号> <x座標n> <y座標n> <z座標n>
−−−−ここまで
計算中のフレーム番号と、座標ファイル中の<フレーム番号>を比較して、一致した時、その座標にメタボールを配置します。例外として、<フレーム番号>が-1だった時、その座標に水たまり用のメタボールを配置することにしました。
よって、座標計算の時、落下して地面に到達した水滴については、以下のように水たまり作成用の座標データを出力するようにしてあります。
if (pz#(idx2)<0)
{
if !(px#(idx2)=0 | py#(idx2)=0 | pz#(idx2)=-1000)
{
// 地面に水滴を残す
mprint lbuf,"# # # #",-1,px#(idx2),py#(idx2),0;
fputl fout,$(lbuf);
}
}
完成したデータと動画をご覧に入れます。
Xi-TRACEデータ:fountain.xitr
噴水の水……、ちょっと苦しい?水滴用のメタボール数:100としましたが、もっと増やして1つ1つの半径を小さくしてやらないとダメかな?だんだんと地面に広がっていく水たまりは、水よりも粘度の高い液体に見えますね。