チュートリアル 12: スクリプトによるレポートのエクスポート

チュートリアル データ

チュートリアル データは、[Help] メニュー → [Download Tutorials and Examples…] を選択し、[CityEngine Tutorial] からダウンロードできます。

概要

Python をベースにしたエクスポート機能を使って CGA レポート変数を使用する方法を学習します。シーン内に散在しているインスタンスの情報をレポートにまとめ、シンプルなテキスト ファイルでインスタンス マップを生成します。CityEngine に特有の Python コマンドについての詳細は CityEngine Python をご参照ください。

Python スクリプティング インターフェイスは、すべてのバージョンの CityEngine で利用できるわけではありません。

演習
Part 1: インスタンス建物の情報をレポートする
Part 2: スクリプト エクスポート
Part 3: 追加エレメント

Part 1: インスタンス建物の情報をレポートする

Part 1 では、シーンの中にインスタンス建物を配置する既存の CGA ファイルを使い、Part 2 の Script Based Exporter で使用するインスタンスの追加情報の準備としてレポート変数を加えます。

本チュートリアルは、スクリプティング API および CGA 言語に関する基礎知識を前提としています。

Script Based Exporter の使用方法

Script Based Exporter は基本的にエクスポート時に実行される CGA 生成プロセスです。エクスポートの設定時に選択したPython スクリプトが、エクスポート/生成の途中および終了後に個々のモデルを処理します。

ここでは以下の作業を行います:

  • 必要な値をレポートするための CGA コマンドを準備する
  • 生成された個々のモデルに対して CGA ルールによりレポートされた値をクエリする
  • すべてのモデルの生成後に収集されたレポートの値をテキスト ファイルに出力する

目的

CityEngine 上で配置された建物インスタンスを、任意のアプリケーション上でロードするのに必要となるデータを含んだテキスト ファイルを作成します。

すべてのインスタンスに対し、次の値を出力します:

  • アセットの識別子 (ファイル名)
  • 3 軸の位置
  • 3 軸の回転
  • 3 軸のスケール

これらを以下の形式で書きます。

nrassetxposxrotxscale
0scifi_building_9.obj-529.480300901.051229466
1scifi_building_17.obj236.614135700.933861537
2scifi_building_5.obj499.424011201.256899709

チュートリアルのセットアップ

  1. CityEngine ワークスペースに Tutorial_12_Scripted_Report_Export をインポートします。
  2. Tutorial_12_Scripted_Report_Export/scenes/reportInstances_01.cej を開きます。

外部 CGA ファイルに汎用レポーティングルールを作成

CGA ファイル instance_city_01.cga に直接必要なレポート コマンドを追加することもできますが、 今回は汎用レポーティング ルールを外部の CGA ファイルに記述します。この方法を使えば、任意の CGA ファイルに対してレポーティング ルールを使用することができます。

  1. [File] メニュー → [New …] → [CityEngine] → [CGA Rule File] で新規ルール ファイルを作成します。
  2. 作成したルール ファイルに instanceReporting.cga と名前を付けます。新規ルール InstanceReport を作成します。ルール パラメーター asset がアセット識別子をレポートするために必要となります。
InstanceReport(asset) -->

変換データのレポート

アセットの識別子 (Asset Identifier)

ルール パラメーター asset をレポートします。

## アセットの識別子をレポート
	report("asset", asset)

スケール (Scale)

多くの場合、元のアセット サイズに対する相対的なスケールが必要となります。そのため、スコープ サイズだけをレポートするのではなく、アセットサイズで除算する必要があります。オリジナル アセット サイズは 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"))

回転 (Rotation)

回転はワールド座標系でレポートする必要があります。したがって、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))

位置 (Position)

位置は一番厄介なパートです。後で他のアプリケーションで使用する際にアセットを正確にインスタンスさせるために、使用アセットのピボットと位置に注意を払うことが特に重要です。今回のケースでは、アセットは地表平面の中心にピボットを持っており、ワールド座標の原点に位置しています。以下の 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 2: スクリプトエクスポート

ここでは、スクリプト ベースのエクスポーターを使用して、Part 1 で用意したレポート データを処理します。このパートでは、その処理を行う新しい Python スクリプトを作成します。

Python スクリプト エクスポート テンプレート

  1. [File] メニュー → [New …] → [Python] → [Python Module] にあるテンプレートから新規 Python エクスポート スクリプトを作成します。
  2. プロジェクトの script フォルダーを選択し、exportinstances と名前を付け、テンプレートとして [Module:Export (Reporting)] を選択します。

テンプレート スクリプトには 4 つの関数が含まれています。今回は finishModel()finishExport() だけを使用するので、他の 2 つは削除します。

finishModel() からレポート データにアクセス

処理中のシェープのレポート変数の配列には、次の方法でアクセスできます。

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()

スクリプトベース エクスポーターの実行

  1. 少数の建物、または lot シェープを選択します。
  2. スクリプト ベース エクスポーターを開始します: [File] メニュー → [Export …] → [CityEngine] → [Export Models of selected Shapes] を選択した後、[Script Based Export (Python)] を選択します。
  3. [Misc. Options] タブで、エクスポート スクリプト exportInstances.py を開き、[Finish] をクリックしてエクスポーターを実行します。

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() 関数

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

processInstance() 関数

この関数は単純にすべてのレポート変数をタブ区切り文字列で返します。

# インスタンス情報をタブ区切り文字列で収集
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

finishExport() 関数

この関数はすべてのシェープ生成が終了した後に呼び出されます。インスタンス マップを含むテキスト ファイルのファイル名を定義し、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() 関数

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()

完成したスクリプトの実行

  1. 建物のセット、または lot シェープを選択します。
  2. スクリプト ベース エクスポーターを開始します。[File] メニュー → [Export …] → [CityEngine] → [Export Models of selected Shapes] を選択し、[Script Based Exporter (Python)] を選択します。
  3. [Misc. Options] タブで、エクスポート スクリプト exportInstances.py を開き、[Finish] をクリックしてエクスポーターを実行します。 結果ファイル instanceMap.txt が現在のプロジェクトの model ディレクトリ内に保存されます。
    nrassetxposxrotxscale
    0scifi_building_9.obj-529.480300901.051229466
    1scifi_building_17.obj236.614135700.933861537
    2scifi_building_5.obj499.424011201.256899709

タブ区切りのテキストファイルに書く代わりに、任意の方法でレポート データを処理することができます。 例えば、インスタンスを作成する Maya 用の Mel スクリプトを書く、データベースに位置情報を書き込む、または独自の ascii 形式で書くなどです。

Part 3: 追加エレメント

汎用 レポート ルールの恩恵により、CGA ルールセットを拡張して追加インスタンス エレメントのレポーティングとエクスポートを行うことができます。

  • instance_city_02.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)
nrassetxpos
0bridge3.obj-363.586952209
1bridge3.obj-313.587295532