Tutorial 12: Scripted report export

To access the tutorials in CityEngine, click Help > Download Tutorials and Examples. When you choose 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. You will report information of instances distributed in a scene, and use it to generate an instance map in a simple text file.

For more information, see Script-based export.

Rendered view of atoll

Report information of instanced buildings

In the first part, you will use an existing CGA file that distributes instanced buildings in the scene and add report variables to prepare additional information of the instances used for the Script-Based Exporter.

This tutorial requires basic knowledge of CGA shape grammar as well as the basics of the scripting API.

Use the Script-Based Exporter

The Script-Based Exporter is a CGA generation process that is triggered via export. The Python script that is set in the export settings and runs along the export and generation process can process each model during and after generation.

In this example, you will do the following:

  • 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

You want to write 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, write the following values:

  • Asset identifier
  • Position in 3 axes
  • Rotation in 3 axes
  • Scale in 3 axes

Write the values in the following form:

nrassetxposxrotxscale

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

Set up

  1. Download the Tutorial_12_Scripted_Report_Export tutorial into your CityEngine workspace.
  2. Open the Tutorial_12_Scripted_Report_Export/scenes/reportInstances_01.cej scene.

Prepare a generic report rule in an external CGA file

Although you can add all the necessary reporting commands directly in the instance_city_01.cga CGA file, you will write an external CGA file with a generic reporting rule. This way, you can use the reporting rule for arbitrary CGA files.

  1. To create a rule file, click File > New > CityEngine > CGA Rule File.
  2. Name the file instanceReporting.cga.

    Create an InstanceReport rule. You need a rule parameter asset to be able to report the asset identifier.

    InstanceReport(asset) -->

Report transformation data

Asset identifier

Report the rule parameter asset.

## report asset identifier
	report("asset", asset)

Scale

In most cases, you need a scale relative to the original asset size. You cannot report the scope size only; you must divide it by the asset size. You can query the original asset size using the assetInfo() command; for example, assetInfo(asset, "sx") queries the size in the x-axis.

The report commands for the scale are as follows:

## 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

To report the rotation in world coordinates, you must 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 most complicated part. To be able to instance your assets correctly in your follow-up application, it is important to pay attention to the pivot and the position of the assets that are used. In this example, the assets have their pivot in their center on the ground plane and are located on the world origin. See the following screenshots from Maya:

Building asset in Maya displaying pivot and position

Before reporting the position, modify the asset scope to be scaled to a small needle and centered in x and z. This ensures that 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 must 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))

You have now reported all the required values. To ensure that no unwanted geometry is displayed in the viewport, add a NIL command to the end for the reporting rule. The final reporting rule is as follows:

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

Use the report rule

Next, you'll return to the original CGA rule file and use the prepared report rule. At the beginning of the instance_city_01.cga file, add the following line to import the prepared report rule file with the instanceReporting ID:

import instanceReporting:"instanceReporting.cga"

Add the InstanceReport rule to the end of the building rule. Be sure to also add the Asset. leaf rule after the insert command to ensure that the asset is generated.

Building(asset) --> 
	s('1,0,'1) 
	i(asset) Asset.
	instanceReporting.InstanceReport(asset)

Generate a building, and review the Reports pane of the Inspector window, which should look similar to the following example:

Report variables shown in the Inspector window

Export the script

You will now process the prepared report data using the Script-Based Exporter. To do this, you need to create a Python script. For more information, see Script-based export.

Python script export template

  1. To create a Python export script from a template, click File > New > Python > Python Module.
  2. Choose the project's script folder, name it exportInstances, and choose Module: Export (Reporting) as the template.
    Python module wizard

The template script contains four functions. You only need finishModel() and finishExport(), so delete the other two.

Access report data in finishModel()

You can access the array of report variables of the currently processed shape as follows, where asset is the name of the report variable:

model.getReports()['asset']

Because not all generated shapes have an instance on them (for example, empty ground shapes or street shapes), you first need to check for the presence of report data using one of the report variables, in this case, '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. You need to loop over all the reported datasets by shape by first getting the length of the 'asset' array. As a first test, print the report data array to the console:

l = len(model.getReports()['asset'])
		for i in range(0,l): 
			print model.getReports()

Run the Script-Based Exporter

  1. Select a small set of buildings or lot shapes.
  2. Start the Script-Based Exporter by clicking File > Export > CityEngine > Export Models for the selected shapes, and choose Script Based Exporter (Python).
  3. On the Misc. Options tab, browse to the exportInstances.py export script and click Finish to run the exporter.
    Script Based Export dialog box with the export script set

    The Python console output should read similar to the following:

    {'zpos': [-362.6108093261719], 'yrot': [-69.42008209228516], 'asset': ...
    {'zpos': [-362.6108093261719], 'yrot': [-69.42008209228516], 'asset': ...
    {'zpos': [-412.1033630371094], 'yrot': [165.30718994140625], 'asset': ...
    ...
    Note:

    You can open the Python output console by clicking Menu > Window > Show Console.

    Select output consoles with the console switch drop-down menu on the toolbar.

Add global variables

Instead of printing the data to the console, you now add a new processInstance() function that will process and collect the report values. You also 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

finishModel() function

The completed finishModel() function is as follows:

# 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

processInstance() function

This function 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

finishExport() function

This function is called after the generation of all shapes is finished. You define the file name 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"

writeFile() function

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

Run the final script

  1. Select a set of buildings or lot shapes.
  2. To start the Script-Based Exporter, click File > Export > CityEngine > Export Models of selected Shapes, and choose Script Based Exporter (Python).
  3. On the Misc. Options tab, browse to the exportInstances.py export script and run the exporter by clicking Finish.

    The resulting instanceMap.txt file is saved in the model directory of the current project.

    nrassetxposxrotxscale

    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 using some other methods, for example, writing a Mel script for Maya that creates the instances, writing position information to a database, or writing a custom ASCII-based format.

Additional elements

Thanks to the generic reporting rule, you can now extend your CGA rule set with additional instance elements for reporting and exporting.

  • Assign the instance_city_02.cga rule to all shapes in the scene.
  • Generate some street shapes.

This rule file includes futuristic arc elements on the streets.

Bridge asset
Bridge asset distributed along streets

Open the CGA rule file instance_city_02.cga.

Use the report rule

To export the bridge instances to the instanceMap, 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)

nrassetxpos...

0

bridge3.obj

-363.586952209

...

1

bridge3.obj

-313.587295532

...

...