チュートリアル データは、[Help] メニュー → [Download Tutorials and Examples…] を選択し、[CityEngine Tutorial] からダウンロードできます。
Python をベースにしたエクスポート機能を使って CGA レポート変数を使用する方法を学習します。シーン内に散在しているインスタンスの情報をレポートにまとめ、シンプルなテキスト ファイルでインスタンス マップを生成します。CityEngine に特有の Python コマンドについての詳細は CityEngine Python をご参照ください。
Python スクリプティング インターフェイスは、すべてのバージョンの CityEngine で利用できるわけではありません。
演習 |
---|
・Part 1: インスタンス建物の情報をレポートする |
・Part 2: スクリプト エクスポート |
・Part 3: 追加エレメント |
Part 1 では、シーンの中にインスタンス建物を配置する既存の CGA ファイルを使い、Part 2 の Script Based Exporter で使用するインスタンスの追加情報の準備としてレポート変数を加えます。
本チュートリアルは、スクリプティング API および CGA 言語に関する基礎知識を前提としています。
Script Based Exporter は基本的にエクスポート時に実行される CGA 生成プロセスです。エクスポートの設定時に選択したPython スクリプトが、エクスポート/生成の途中および終了後に個々のモデルを処理します。
ここでは以下の作業を行います:
CityEngine 上で配置された建物インスタンスを、任意のアプリケーション上でロードするのに必要となるデータを含んだテキスト ファイルを作成します。
すべてのインスタンスに対し、次の値を出力します:
これらを以下の形式で書きます。
nr | asset | xpos | xrot | xscale |
---|---|---|---|---|
0 | scifi_building_9.obj | -529.4803009 | 0 | 1.051229466 |
1 | scifi_building_17.obj | 236.6141357 | 0 | 0.933861537 |
2 | scifi_building_5.obj | 499.4240112 | 0 | 1.256899709 |
CGA ファイル instance_city_01.cga に直接必要なレポート コマンドを追加することもできますが、 今回は汎用レポーティング ルールを外部の CGA ファイルに記述します。この方法を使えば、任意の CGA ファイルに対してレポーティング ルールを使用することができます。
InstanceReport(asset) -->
ルール パラメーター asset をレポートします。
## アセットの識別子をレポート
report("asset", asset)
多くの場合、元のアセット サイズに対する相対的なスケールが必要となります。そのため、スコープ サイズだけをレポートするのではなく、アセットサイズで除算する必要があります。オリジナル アセット サイズは assetInfo() コマンドを使用して調べることができます。assetInfo(asset, “sx”) は x 軸方向のサイズを調べます。 スケール用の report コマンドは以下です:
## アセットに対する相対スケール値をレポート
report("xscale", scope.sx/assetInfo(asset, "sx"))
report("yscale", scope.sy/assetInfo(asset, "sy"))
report("zscale", scope.sz/assetInfo(asset, "sz"))
回転はワールド座標系でレポートする必要があります。したがって、convert() コマンドを使用して、ピボットの回転パラメーターを変換します。
## ワールド座標で回転をレポート
report("xrot", convert(x, pivot, world, orient, 0,0,0))
report("yrot", convert(y, pivot, world, orient, 0,0,0))
report("zrot", convert(z, pivot, world, orient, 0,0,0))
位置は一番厄介なパートです。後で他のアプリケーションで使用する際にアセットを正確にインスタンスさせるために、使用アセットのピボットと位置に注意を払うことが特に重要です。今回のケースでは、アセットは地表平面の中心にピボットを持っており、ワールド座標の原点に位置しています。以下の Maya の画面スクリーンショットを参照してください。
位置をレポートする前に、以下の方法でアセット スコープを変更します。スコープは小さな ”針”くらいまで縮小され、x と z の中心に配置されます。この方法により、レポートされる位置を Maya 上のアセットのピボットと確実に一致させることができます。
## スコープのスケーリングとセンタリング
s(0.0001,'1,0.0001) center(xz)
ここで位置をもう一度、ワールド座標に変換する必要があります。
## ワールド座標で位置をレポート
report("xpos", convert(x, scope, world, pos, 0,0,0))
report("ypos", convert(y, scope, world, pos, 0,0,0))
report("zpos", convert(z, scope, world, pos, 0,0,0))
これでレポートに必要なすべての値がそろいました。[Viewport] ウィンドウに不必要なジオメトリを一切表示しないようにするため、 NIL コマンドをレポート ルールの最後に加えます。最終的なレポート ルールは以下のようになります。
InstanceReport(asset) -->
## アセットの識別子をレポート
report("asset", asset)
## アセットに対する相対スケール値をレポート
report("xscale", scope.sx/assetInfo(asset, "sx"))
report("yscale", scope.sy/assetInfo(asset, "sy"))
report("zscale", scope.sz/assetInfo(asset, "sz"))
## ワールド座標で回転をレポート
report("xrot", convert(x, pivot, world, orient, 0,0,0))
report("yrot", convert(y, pivot, world, orient, 0,0,0))
report("zrot", convert(z, pivot, world, orient, 0,0,0))
## スコープのスケーリングとセンタリング
s(0.001,'1,0.001) center(xz)
## ワールド座標で位置をレポート
report("xpos", convert(x, scope, world, pos, 0,0,0))
report("ypos", convert(y, scope, world, pos, 0,0,0))
report("zpos", convert(z, scope, world, pos, 0,0,0))
NIL
最初の CGA ルール ファイルに戻り、作成したレポート ルールを使ってみましょう。instance_city_01.cga ファイルの最初に、 instanceReporting という識別子を含んだレポート ルール ファイルをインポートするため、次の一文を加えます。
import instanceReporting:"instanceReporting.cga"
そして、InstanceReport ルールを建物ルールの最後に加えます。アセットが生成されたことを確認するために、insert コマンドの後ろに終端ルールとして Asset. も追加します。
Building(asset) -->
s('1,0,'1)
i(asset) Asset.
instanceReporting.InstanceReport(asset)
建物を生成すると、[Inspector] ウィンドウの Reports 欄では以下のようになっているはずです:
[Inspector] ウィンドウの Reports 欄に表示されるのは統計情報のみであり、全ての値を表示しているわけではありません (例えば文字列は表示されません) が、今回のケースのように、必要なすべての値がレポートされていることを簡易に確認するために使用することができます。
ここでは、スクリプト ベースのエクスポーターを使用して、Part 1 で用意したレポート データを処理します。このパートでは、その処理を行う新しい Python スクリプトを作成します。
テンプレート スクリプトには 4 つの関数が含まれています。今回は finishModel() と finishExport() だけを使用するので、他の 2 つは削除します。
処理中のシェープのレポート変数の配列には、次の方法でアクセスできます。
model.getReports()['asset']
ここで記述した asset はレポート変数の名前です。
生成されたすべてのシェープ (例えば空のグラウンド シェープや道路シェープなど) がインスタンスを持っているわけではないので、まずはレポート変数の 1 つである「asset」を使って、レポート データの有無を確認する必要があります。
if(model.getReports().has_key('asset')):
生成された建物の CGA ルール をよく確認すると、いくつかのシェープが 1 つ以上のインスタンスを含んでいることが分かります。そのため、最初に「asset」配列の長さを取得し、シェープごとにすべてのレポートされたデータセットに対してループ処理をかける必要があります。最初のテストとしてコンソールにレポート データ配列をプリントします。
l = len(model.getReports()['asset'])
for i in range(0,l):
print model.getReports()
Python コンソールには次のようなものが表示されるはずです。
{'zpos': [-362.6108093261719], 'yrot': [-69.42008209228516], 'asset': ...
{'zpos': [-362.6108093261719], 'yrot': [-69.42008209228516], 'asset': ...
{'zpos': [-412.1033630371094], 'yrot': [165.30718994140625], 'asset': ...
...
Python コンソールは [Window] メニュー → [Show Console] から開くことができます。コンソール切り替えドロップ ダウンから出力先コンソールを選択できます。
コンソールにデータを印刷する代わりに、レポートの値を処理・収集する新しい関数 processInstance() を追加します。また、データを収集し、インスタンスのカウントを記録する二つのグローバル変数 (finishModel 関数外で) を追加します。
# グローバル変数
gInstanceData = "" # 書き出される全データを収集するグローバル変数 (文字列)
gInstanceCount = 0 # 全インスタンスに番号を振るグローバル変数 (カウント)
finishModel() 関数の完成形
# 生成後の各初期シェープに対して呼び出し
def finishModel(exportContextOID, initialShapeOID, modelOID):
global gInstanceData, gInstanceCount
model = Model(modelOID)
if(model.getReports().has_key('asset')): # レポート データが存在する場合に t3d エントリのみ書き出し
# モデルに対して複数のアセットが存在する場合があるのでループを使用する
l = len(model.getReports()['asset'])
for i in range(0,l):
instanceData = processInstance(model.getReports(),gInstanceCount, i-1)
gInstanceData = gInstanceData+instanceData
gInstanceCount = gInstanceCount+1
この関数は単純にすべてのレポート変数をタブ区切り文字列で返します。
# インスタンス情報をタブ区切り文字列で収集
def processInstance(reports, count, index):
## asset 文字列からパスを削除
asset = reports['asset'][index]
asset = asset.rpartition("/")[2]
## インスタンス マップ用の文字列を用意
text = "%d\t" % count;
text += "%s\t" % asset;
text += "%.3f\t%.3f\t%.3f\t" % (reports['xpos'][index],reports['ypos'][index], reports['zpos'][index])
text += "%.3f\t%.3f\t%.3f\t" % (reports['xrot'][index],reports['yrot'][index], reports['zrot'][index])
text += "%.3f\t%.3f\t%.3f\n" % (reports['xscale'][index], reports['yscale'][index], reports['zscale'][index])
return text
この関数はすべてのシェープ生成が終了した後に呼び出されます。インスタンス マップを含むテキスト ファイルのファイル名を定義し、writeFile() 関数を呼び出します。
# すべての初期シェープが生成された後で呼び出される。
def finishExport(exportContextOID):
global gInstanceData, gInstanceCount
## 出力ファイルのパス
file = ce.toFSPath("models")+"/instanceMap.txt"
## 収集されたデータをファイルに書き出し
writeFile(file, gInstanceData)
print str(gInstanceCount)+"instances written to "+file +"\n"
writeFile() 関数はヘッダー情報を加え、収集したレポート文字列をディスクに書き込みます。
# データ (ヘッダーと内容) を結合し、ファイルに書き出し
def writeFile(file, content):
## ヘッダー文字列を準備
head = "nr\tasset\txpos\typos\tzpos\txrot\tyrot\tzrot\txscale\tyscale\tzscale\n"
## ヘッダーと内容を結合
content = head + content
## データをファイルに書き出し
report = open(file, "w")
report.write(content)
report.close()
nr | asset | xpos | xrot | xscale |
---|---|---|---|---|
0 | scifi_building_9.obj | -529.4803009 | 0 | 1.051229466 |
1 | scifi_building_17.obj | 236.6141357 | 0 | 0.933861537 |
2 | scifi_building_5.obj | 499.4240112 | 0 | 1.256899709 |
タブ区切りのテキストファイルに書く代わりに、任意の方法でレポート データを処理することができます。 例えば、インスタンスを作成する Maya 用の Mel スクリプトを書く、データベースに位置情報を書き込む、または独自の ascii 形式で書くなどです。
汎用 レポート ルールの恩恵により、CGA ルールセットを拡張して追加インスタンス エレメントのレポーティングとエクスポートを行うことができます。
このルール ファイルには道路上に未来的な円弧エレメントが含まれています。
CGA ルール ファイル instance_city_02.cga を開きます。
インスタンス マップに橋インスタンスをエクスポートするには、InstanceReport ルールを Bridge ルールに加えるだけでできます。
Bridge -->
s(1,8,scope.sz+3.2) center(xz)
i(bridge_asset)
s(calcHeight2(scope.sx),calcHeight2(scope.sy),'1)
Bridge.
instanceReporting.InstanceReport(bridge_asset)
nr | asset | xpos | … |
---|---|---|---|
0 | bridge3.obj | -363.586952209 | … |
1 | bridge3.obj | -313.587295532 | … |
… |