今回は、ポリゴンスクリプトを使って、子オブジェクトをコピーするクリエータオブジェクトを作成してみます。
ポリゴンスクリプトで、必ず定義しなければならない関数は2つ
まずエディタで新規ファイルを作成し、以下のコードをコピペして下さい。以下のコードでは、とりあえず空の関数を定義しています。
function buidUI( obj ) {
}
function buildObject( obj ) {
}
2つの関数とも、引数として自身のオブジェクトの参照が渡されます。buildUI 関数では、パラメータの追加や初期化を行い、 buildObject 関数内で、実際の描画コードを記述していきます。buildUI 関数は、スクリプトメニューから選択されてシーンに呼び出されたときに1度、buildObject 関数は、自身のパラメータが変更されるタイミング意外にも、親オブジェクトの更新といったシーングラフの更新時にも毎回呼ばれます。
まず、buildUI 関数内では、自身のプロパティを定義します。
function buildUI( obj ) {
// 初期値 1 最小値 1 最大値 10 の整数型パラメータを追加
obj.addParameterInt('count',1,1,10,true,true);
}
ここでは 'count' という名前の整数のパラメータを追加しています。addParameterInt 関数の場合、引数は順に、パラメータ名・初期値・最小値・最大値・アニメーション可/不可フラグ・ビルドフラグ(このパラメータが変更された場合 buildObject を呼び出してシーングラフを更新するかどうか)になります。
ここで、一度ファイルを保存しシーンに呼び出してみます。ポリゴンスクリプトの場合、~/ライブラリ/Application Support/Cheetah3D/scripts/Polygonobj フォルダに保存します。この時、拡張子は js にして下さい。一度スクリプトメニューから、シーンに呼び出してみましょう。 buildObject 関数内には、まだ何も書いてないので 3D ビューには何も描画はされませんが、オブジェクトリストビューには、スクリプトオブジェクトが登場していると思います。'count' パラメータが、ポリゴンスクリプトのプロパティパネルに表示されていることを確認して下さい。
その他、プロパティにパラメータを追加する関数は以下のようなものがあります。これらの関数は、プロパティを定義できる他のタイプのスクリプト(スプライン/ツール)でも共通です。
それでは、実際の描画コードを書いていきましょう。ポリゴンオブジェクトをスクリプトから操作するには、まずポリゴンオブジェクトから PolyCore オブジェクトを取り出し、その PolyCore オブジェクトに対して描画関数を実行していきます。
3角形を描くコードは下記のようになります。
function buildObject ( obj ) {
// ポリゴンオブジェクトに定義されている core() 関数を使用して、PolyCore オブジェクトを取得
var core = obj.core()
// ポリゴンを構成する頂点を配列で定義、とりあえず単純な3角形
// xyz 座標を指定する Vec3D 型の配列を使用
var vertices = [ new Vec3D(0, 0, 0), new Vec3D(0, 1, 0), new Vec3D(1, 0, 0) ];
// 一番シンプルなポリゴン生成関数を使用します
core.addPolygon( vertices.length, false, vertices );
}
ここスクリプトメニューから、シーンに追加してみましょう。3角形が表示されていると思います。
ポリコア PolyCore オブジェクトにポリゴンを追加する関数には2種類あります。
また addIndexPolygon で使用する頂点(ポイント)のみを追加する関数は addVertex を使用します。
addPolygon や addVertex で既存の頂点を捜査して頂点を追加する場合、頂点数が多くなった場合に捜査に時間がかかり動作が遅くなります。 buildVertexBSP 関数を使って、オブジェクト内の頂点データの BSP ツリーを生成すると、頂点の捜査が速くなります。
// BSP ツリーの最小値と最大値を設定
// ポリゴンを生成する空間の最小値と最大値を設定して BSP ツリーを生成
// ここで空間(最小値と最大値)を大きく取りすぎると捜査の精度が落ちるので注意
buildVertexBSP( new Vec3D( 0, 0, 0), new Vec3D( 2, 2, 2) );
/*
addVertex や addPolygon で meshed を true にしてポリゴンを生成
*/
// 生成が終わった段階で、必ず BSP ツリーを破棄してメモリを解放すること
destroyVertexBSP();
今回は、子オブジェクトのデータをそのままコピーするので、頂点追加時に既存の頂点(ポイント)を捜査する必要はありません。なので、BSP ツリーは使いません。 :-P
次に子オブジェクトの情報からポリゴンを生成してみます。
function buildObject( obj ) {
// 小オブジェクトがない場合、何もしない
if ( obj.childCount() == 0) return;
// 子オブジェクトを取得し、ポリゴンファミリーでない場合、何もしない
var child = obj.childAtIndex(0);
if ( child.family() != NGONFAMILY) return;
// ここまでエラーチェック
//
// for ループ用の変数
var i,j;
// 子オブジェクトの PolyCore を取得
var child_core = child.core();
// モディファイア適用後の PolyCore を使いたい場合は、modCore() を使用
// var child_core = child.modCore();
// 自身のコアを取得
var core = obj.core();
// 頂点をコピー
var vertexCount = child_core.vertexCount();
for (i = 0;i < vertexCount;i++) {
// 子オブジェクトからのコピーなので、第1引数は false (頂点捜査はしない)
core.addVertex(false, child_core.vertex(i));
}
// ポリゴンをコピー
var polyCount = child_core.polygonCount();
var polySize = 0;
for (i = 0;i < polyCount;i++) {
var indices = [];
polySize = child_core.polygonSize(i);
for (j = 0;j < polySize;j++) {
indices.push( child_core.vertexIndex( i, j ) );
}
var pi = core.addIndexPolygon( polySize, indices );
// UV 座標をコピー
for (j = 0;j < polySize;j++) {
core.setUVCoord(pi, j, child_core.uvCoord(i, j));
}
}
}
ここまでを、再度シーンに呼び出してみます。子オブジェクトを追加して、子オブジェクトのモードタグで不可視に設定します。そのままでは、オブジェクトの更新がかからず何も描画されていないと思うので、'count' 等のパラメータを変更して、強制的にオブジェクトをアップデートしてみて下さい。子オブジェクトと同じ形状が表示されました?
いちいち描画更新のために、パラメータをいじるのが面倒なので、このオブジェクトをクリエータオブジェクトに設定しておきます。
function buildUI ( obj ) {
// 初期値 1 最小値 1 最大値 10 の整数型パラメータを追加
obj.addParameterInt("count",1,1,10,true,true);
// クリエータオブジェクトに設定
obj.setCreatorObj( true );
}
これで、子オブジェクトは自動的に非表示になり、子オブジェクトの変更で自身がアップデートされるクリエータオブジェクトとして振る舞うよう設定されました。
ここまでだと、単に子オブジェクトをコピーするだけなので、全くスクリプトを使う意味がありませんよね。 :-P
次に、追加しておいた 'count' パラメータを使って、 count 分だけランダムにコピーするように変更してみます。
function buildObject( obj ) {
// 小オブジェクトがない場合、何もしない
if ( obj.childCount() == 0) return;
// 子オブジェクトを取得し、ポリゴンファミリーでない場合、何もしない
var child = obj.childAtIndex(0);
if ( child.family() != NGONFAMILY) return;
// ここまでエラーチェック
// count を取得
var count = obj.getParameter("count");
// 子オブジェクトの PolyCore を取得
var child_core = child.core();
// モディファイア適用後の PolyCore を使いたい場合は、modCore() を使用
/*
var child_core = child.modCore();
*/
// 自身のコアを取得
var core = obj.core();
for (var i = 0;i < count;i++) {
var vec = new Vec3D( Math.random() * 5 - 2.5, Math.random() * 5 - 2.5, Math.random() * 5 - 2.5);
// ポリゴンコピーのコードを関数化して外に
copyPolyCore( core, child_core, vec );
}
}
function copyPolyCore( core, child_core, vec ) {
// ほぼ同じコード
var i,j;
// 頂点をコピー
// 前のコピーで追加された頂点数オフセットとして取得
var vertexCount_offset = core.vertexCount();
var vertexCount = child_core.vertexCount();
for (i = 0;i < vertexCount;i++) {
// 子オブジェクトからのコピーなので、頂点は捜査しない。
// 第3引数の vec (ランダム要素)を加算してコピー
core.addVertex(false, child_core.vertex(i).add(vec) );
}
// ポリゴンをコピー
var polyCount = child_core.polygonCount();
var polySize = 0;
for (i = 0;i < polyCount;i++) {
var indices = [];
polySize = child_core.polygonSize(i);
for (j = 0;j < polySize;j++) {
indices.push( vertexCount_offset + child_core.vertexIndex( i, j ) );
}
var pi = core.addIndexPolygon( polySize, indices );
// UV 座標をコピー
for (j = 0;j < polySize;j++) {
core.setUVCoord(pi, j, child_core.uvCoord(i, j));
}
}
}
これで、'count' で設定した数だけランダムな位置にコピーするスクリプトオブジェクトが出来ました。
これにさらに調整できるパラメータを追加したものを Ramdom Array.js として公開していますので、興味のある方は是非チェックしてみて下さい。(古いコードなので、ちょっとあれですけど… :-! )
ちょっと長過ぎたかも。 :-(
読んでいただいた皆様ありがとうございます。