To access the tutorials in CityEngine, click Help > Download Tutorials and Examples.... After choosing a tutorial or example, the project is automatically downloaded and added to your workspace.
This tutorial shows how to use CGA report variables in combination with the Python-based exporter. We will report information of instances distributed in our scene, and use it to generate an instance map in a simple text file.
More information on the CityEngine-specific Python command set can be found in the CityEngine help: Help > Help Contents > Manual > Python Scripting.
Note:
The Python Scripting Interface is not available in all CityEngine versions.
Part 1: Report Information of Instanced Buildings
In the first part, we will use an existing CGA file that distributes instanced buildings in our scene and add report variables to prepare additional information of the instances used for the Script Based Exporter in part 2.
This tutorial requires a basic knowledge of CGA Shape Grammar as well as the basics of the Scripting API.
Using the Script Based Exporter
The Script Based Exporter is basically a CGA generation process that is triggered via export. The Python script that is set in the export settings and runs along the export/generation process can process each model during and after generation.
In this example we are going to:
- Prepare CGA commands that report the required values.
- Query the values reported in CGA rules for each generated model.
- Write a text file with the collected report values after all models are generated.
Goal
We want to write out a text file that contains the necessary data to be able to load the distributed building instances in arbitrary follow-up applications.
For every instance, the following values should be written:
- asset identifier
- position in 3 axes
- rotation in 3 axes
- scale in 3 axes
In the following form:
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 |
Tutorial Setup
- Import the project Tutorial_12_Scripted_Report_Export into your CityEngine workspace.
- Open scene Tutorial_12_Scripted_Report_Export/scenes/reportInstances_01.cej.
Preparing a Generic Report Rule in an External CGA File
Although we could add all the necessary reporting commands directly in the CGA file instance_city_01.cga, we will write an external CGA file with a generic reporting rule. This way, we will be able to use the reporting rule for arbitrary CGA files.
- Create a new rule file File > New ... > CityEngine > CGA Rule File.
- And name it instanceReporting.cga.
Create a new rule InstanceReport. We need a rule parameter asset to be able to report the asset identifier.
InstanceReport(asset) -->
Reporting Transformation Data
Asset Identifier
We simply report the rule parameter asset.
## report asset identifier
report("asset", asset)
Scale
In most cases one needs a scale relative to the original asset size. We therefore cannot report the scope size only, but need to divide it by the asset size. The original asset size can be queried using the assetInfo() command. assetInfo(asset, "sx") queries the size in x-axis.
The report commands for the scale are therefore:
## report scale values relative to asset
report("xscale", scope.sx/assetInfo(asset, "sx"))
report("yscale", scope.sy/assetInfo(asset, "sy"))
report("zscale", scope.sz/assetInfo(asset, "sz"))
Rotation
We want to report the rotation in world coordinates, and therefore need to convert the pivot rotation in CityEngine using the convert() command:
## report rotation in world coords
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
Position is the trickiest part. To be able to instance your assets correctly in your follow-up application, it is crucial to pay attention to the pivot and the position of the used assets. In our case the assets have their pivot in their center on the ground plane, and are located on the world origin. See the Maya screenshot below for clarification:
Before reporting the position, we will therefore modify the asset scope in the following way: The scope is scaled to a small "needle", and centered in x and z. This way we make sure the reported position corresponds to the pivot of the asset in Maya.
## scale and center scope
s(0.0001,'1,0.0001) center(xz)
Again, the position needs to be converted to world coordinates:
## report position in world coords
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))
We have now reported all the required values. To make sure no undesired geometry is displayed in the viewport, we add a NIL command to the end for the reporting rule. The final reporting rule:
InstanceReport(asset) -->
## report instance ID
report("asset", asset)
## report scale values relative to asset
report("xscale", scope.sx/assetInfo(asset, "sx"))
report("yscale", scope.sy/assetInfo(asset, "sy"))
report("zscale", scope.sz/assetInfo(asset, "sz"))
## report rotation in world coords
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))
## scale and center scope
s(0.001,'1,0.001) center(xz)
## report position in world coords
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
Using the Report Rule
Let's get back to our original CGA rule file and use the prepared report rule. At the beginning of the file instance_city_01.cga, add the following line to import the prepared report rule file with the id instanceReporting.
import instanceReporting:"instanceReporting.cga"
Now we simply add the InstanceReport rule to the end of our building rule. Make sure to also add the leaf rule “Asset.” after the insert command to make sure the asset is generated.
Building(asset) -->
s('1,0,'1)
i(asset) Asset.
instanceReporting.InstanceReport(asset)
Generate a building now, and look into the Reports pane of the Inspector, which should look similar to this:
Note:
The Reports pane in the Inspector is only for statistical reports, and does not display all values (e.g. does not display strings). However, it can be used in our case to make sure all required values are reported.
Part 2: The Export Script
We will now process the prepared report data using the Script Based Exporter. For this, we need to create a new Python script which will do the job.
Python Script Export Template
- Create a new python export script from template File > New ... > Python > Python Module.
- Choose the project's script folder, name it exportInstances and choose Module: Export (Reporting) as template.
The template script contains four functions. We only need finishModel() and finishExport(), so delete the other two.
Access Report Data in finishModel()
The array of report variables of the currently processed shape can be accessed in the following way:
model.getReports()['asset']
Where asset is the name of the report variable.
Because not all generated shapes have an instance on them (e.g. empty ground shapes or street shapes), we first need to check for the presence of report data using one of the report variables, namely 'asset':
if(model.getReports().has_key('asset')):
If you look at the CGA rules of the generated buildings in detail, you will notice that some shapes contain more than one instance. We therefore need to loop over all the reported datasets per shape by first getting the length of the 'asset' array. As a first test, we will print out the report data array to the console:
l = len(model.getReports()['asset'])
for i in range(0,l):
print model.getReports()
Running the Script-Based Exporter
- Select a small set of buildings or lot shapes
- Start the script based exporter: File > Export ... > CityEngine > Export Models of selected Shapes and choose Script Based Exporter (Python)
- In the Misc. Options tab, browse to the export script exportInstances.py and run the exporter by clicking Finish.
The Python console output should read something of the form:
{'zpos': [-362.6108093261719], 'yrot': [-69.42008209228516], 'asset': ...
{'zpos': [-412.1033630371094], 'yrot': [165.30718994140625], 'asset': ...
...
Note:
The Python output console can be opened via Menu Window > Show Console.
Select output consoles with the console switch dropdown on the toolbar.
Adding Globals
Instead of printing the data to the console, we now add a new function processInstance() that will process and collect the report values. Additionally we add two global variables (outside the finish model function) that collect the data and keep track of the instance count:
# Globals
gInstanceData = "" # global string that collects all data to be written
gInstanceCount = 0 # global count to enumerate all instances
The finishModel() Function
The completed function finishModel():
# Called for each initial shape after generation.
def finishModel(exportContextOID, initialShapeOID, modelOID):
global gInstanceData, gInstanceCount
model = Model(modelOID)
if(model.getReports().has_key('asset')): # only write t3d entry if report data available
# there might be more than one asset per model, therefore loop
l = len(model.getReports()['asset'])
for i in range(0,l):
instanceData = processInstance(model.getReports(),gInstanceCount, i-1)
gInstanceData = gInstanceData+instanceData
gInstanceCount = gInstanceCount+1
The processInstance() Function
This function simply returns all the report variables in a tab-separated string:
''' Collect the instance information in a tab-separated string'''
def processInstance(reports, count, index):
## remove path from asset string
asset = reports['asset'][index]
asset = asset.rpartition("/")[2]
## prepare the string for the instance map
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
The finishExport() Function
This function is called after the generation of all shapes is finished. We define the filename of the text file containing the instance map here, and call the writeFile() function:
# Called after all initial shapes are generated.
def finishExport(exportContextOID):
global gInstanceData, gInstanceCount
## path of the output file
file = ce.toFSPath("models")+"/instanceMap.txt"
## write collected data to file
writeFile(file, gInstanceData)
print str(gInstanceCount)+"instances written to "+file +"\n"
The writeFile() Function
... adds the header information and writes the collected report string to disk:
''' combining data (header and content) and writing file '''
def writeFile(file, content):
## prepare the head string
head = "nr\tasset\txpos\typos\tzpos\txrot\tyrot\tzrot\txscale\tyscale\tzscale\n"
## combine header and content
content = head + content
## write data to the file
report = open(file, "w")
report.write(content)
report.close()
Running the Final Script
- Select a set of buildings or lot shapes.
- Start the script based exporter: File > Export ... > CityEngine > Export Models of selected Shapes and choose Script Based Exporter (Python).
- In the Misc. Options tab, browse to the export script exportInstances.py and run the exporter by clicking Finish.
The resulting file instanceMap.txt is saved in the model directory of the current project.
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
Instead of writing a tab-separated text file, you can process the reported data in arbitrary ways: E.g. writing a Mel script for Maya that creates the instances, writing position information to a database or writing your custom ascii-based format.
Part 3: Additional Elements
Thanks to the generic reporting rule, we can now easily extend our CGA rule set with additional instance elements for reporting and export.
- Assign instance_city_02.cga to all shapes in the scene.
- Generate some street shapes
This rule file includes futuristic arc elements on the streets.
Open the CGA rule file instance_city_02.cga.
Using the Report Rule
In order to export the bridge instances to the instanceMap as well, simply add the InstanceReport rule to the Bridge rule.
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 | ... |
... |