From CityEngine to Unreal Studio: the journey from first design steps to high-quality real-time visualization

Document created by imitropoulou-esristaff Employee on May 23, 2019Last modified by SArisona-esristaff on Jun 20, 2019
Version 7Show Document
  • View in full screen mode

We are excited to present an example project from first design steps using ESRI CityEngine, to high-end visualization using Unreal Studio, Epic Games’ enterprise solution based on their Unreal Engine 4 game engine. Our motivation for this project is to show the new possibilities that come along with the latest releases of CityEngine 2019 and Unreal Engine 4.22, and explore the creative potential that is revealed by linking these two engines.

 

Our workflow consists of two parts: procedural design, with CityEngine 2019 as the main tool and visualization strategies with Unreal Engine 4.22 as the main tool. For each part we discuss our approach conceptually and then give technical details for the implementation of our ideas, along with tips for best practices and links to relevant pages for more in depth exploration of the various techniques.

 

South Boston CityEngine project now available on request!
Are you interested using the CityEngine project presented in this document? Please send an email to Stefan Arisona and we'll send you a download link.

 

 

 


Background: a plan for redevelopment of Dorchester Avenue in South Boston

 

The project for the redevelopment of Dorchester Avenue in South Boston was carried out by the Boston Planning & Development agency in 2017. It consists of a zoning framework for future growth of an area in South Boston in a manner that is consistent with the community's vision. Their vision describes a walk-able mixed-use neighborhood with improved public transportation, big sidewalks and cycling routes, that favors green spaces and view corridors along the main transportation axes.

 

> More information about the plan for the redevelopment of Dorchester Avenue by the Boston Planning & Development agency 

 

Inspired by this plan and informed by its main guidelines, we went on to design detailed buildings and public spaces for the area using procedural tools. Our main goal was the creation of abundant green spaces on the street level as well as on the buildings in order to create an interconnected network of natural spaces serving various purposes and achieve integration of green and grey infrastructure.  As a result, we focused on creating green corridors and building types that accommodate green spaces on the ground level as well as along their height. We were also interested in accentuating the view corridors that were proposed in the plan by specifying detailed setback rules for the buildings that are located on those corridors.

 

Procedural design with CityEngine 

 

CityEngine is a tool that allows to generate shapes procedurally, i.e. by writing the rules that describe them instead of creating their geometry directly. This strategy, also known as grammar-based modeling is particularly useful when shapes that obey certain standardized rules need to be created in large numbers, which makes it very suitable for the generation of urban environment. CityEngine offers its own programming language, called CGA shape grammar, that is specifically created for writing rules for the generation of architectural 3D content.

 

> CityEngine Tutorials: Here you can find a series of tutorials that walks you through the main aspects of working in the CityEngine, and the basics of procedural modelling using CGA rules.

 

Getting environment data

 

 

The initialization of the design environment in CityEngine can be easily done by importing data from ArcGIS or other online sources. This brings in streets, blocks, parcels, existing buildings, height-map and other elements along with their attributes for further processing.

 

> More information about importing data to CityEngine:

  Video tutorial: CityEngine How-To | Get Map Data 

  ArcGIS Blog post: Import Terrain, Imagery & OSM data into CityEngine

  CityEngine tutorial: import streets

 

Refining urban layout

 

Street networks can be downloaded or imported from a variety of sources, as well as generated using the street grow feature. The street network geometry and attributes create the urban layout: street lanes, sidewalks and parcels. CityEngine offers a variety of tools for refining the street network both manually (such as drawing and editing streets, and creating streets on multiple levels) as well as automatically (such as cleaning up streets, aligning streets to terrain, or simplifing street graphs). Analysis tools are also available: computation of global integration, local integration and in-between centrality.

 

CGA rules 

 

Modular approach

 

Once the context has been prepared, we can start creating the rules and applying them to the urban layout shapes for the generation of the city. We started experimenting with simple rules but soon it became clear that in order to achieve the desired details and diversity more complex CGA code would be required. Before going any further, we took some time to think of a way to organize that code to make sure that it doesn’t become hard to handle. Thus we decided to structure the shape grammar code in a modular way: separate the various aspects of our design in smaller blocks of code, which are then connected to generate the buildings and the environment. These blocks of code that form separate CGA rules can be combined in various ways to achieve more morphological diversity with less coding through different combinations.

 

The typology we came up with is organized by means of several categories: envelope; volume; façade; green space; landscape; ground floor plan; interior plan; street front. For each category we created a number of design alternatives based on the use such as residential; mixed use; commercial; industrial, or based on the position such as standard; on view corridor; transitory.

 

 

 

Envelope

The envelope represents the building’s outer shape if it were to develop to the maximum of its capacity according to the city rules. The parameters set in the envelope are the height, offset from street, shape, setbacks, lot coverage and orientation of the building. We specified different types of envelopes according to the position of the building in regards to the street network: standard, on view corridor or transitory, and we also added exceptions.

 

 

Volume

The volume rules are subtractive operations on the mass of the envelope that refine the outer shape of the building and determine the position of walls, doors, windows, slabs, green space etc.

 

Façade

The façade rules add detail to the building elements that were defined in the volume. For example, the surface that represents the position of a window is replaced by glass, frame and shading elements.

 

Green space

The green space distributes plants in the designated areas using CityEngine built-in plant libraries.

 

 

Ground floor plan

The ground floor plan generates a 3D plan and building structure on the ground floors from the edges of the building envelope, in order to enhance the street level views.

 

 

Interior plan

Walls and structural elements are generated in the interior of buildings that have large glazing surfaces on their facade, in order to create depth and shadows for more realistic visualization. 

 

 

Landscape

The landscape rules refine the free space around the buildings. While CGA rules are very handy for creating straight lines, they have limitations in the creation of curves and free form shapes. A strategy to overcome this is to create OBJ assets using a 3D modeling tool (in our case Rhino-Grasshopper) that are then imported in CityEngine as start shapes and are then processed further with CGA rules. 

 

 

 

Organisation of CGA rules 

 

Now that we have seen what blocks of code we have, let’s take a look at how these are connected. This can be done in various ways. In our project we chose to have as the assigned rule file the envelope and make all other rules dependent on it. The envelope controls the attributes (for example height, shape, use of floors, level of detail), assigns further rules according to the building’s specific attributes and determines what shapes will be passed on to each rule. Parameters are also propagated from the envelope to the other rules.

 

 

Organizing the code with one main rule that assigns sub-rules has many advantages. Firstly, features of the building can be turned on and off at any moment, so the user can choose which level of abstraction to work on. For example, it is possible to work on the massing of the buildings (envelope rule) at any given moment without having to generate fully detailed buildings, by turning off all the subsequent rules. Also, the buildings can change forms and characteristics just by changing attributes, without the need to assign a different rule file. What is more, combining smaller rules instead of creating big end-to-end rules leads to the formation of small rules that are easier to isolate, modify and debug.

 

Example

We present a simplified example for the generation of a building that is created with the logic described above, with an envelope, a volume, and a façade rule.

We start by creating three new CGA files in our rules folder in a CityEngine project. 

The rule file envelope.cga creates the outer shape of the building.

/**
* File:    envelope.cga
*/


version "2019.0"

attr lot_coverage_parameter = 0.50
attr building_height = 40

Lot -->
    offset(-rand(2,3.5))
    comp(f){inside: Lot_for_building
           |border: Free_space}

Lot_for_building -->
    50%: #orthogonal shape
        alignScopeToAxes(y)
        center(xz)
        split(x){'lot_coverage_parameter: Footprint
                |~1                     : Free_space}

    else: #shapeL
        alignScopeToAxes(y)
        shapeL(lot_coverage_parameter*scope.sz, lot_coverage_parameter*scope.sx)
            {shape    : Footprint
            |remainder: Free_space}

Free_space -->
    color("#4A966C")

Footprint -->
    extrude(building_height)
    Complete_envelope

 

Then we want to call our last shape, or leaf, called ‘Complete_envelope’ in the next rule (volume.cga) for further processing. The first thing we need to do is connect the envelope with the volume and façade rules, so we import them at the beginning of envelope.cga. Note that if you drag and drop the rule files from the navigator into the file envelope.cga then the imports appear automatically. And then we add the rule that points towards the file volume.cga

 

/**
* File: envelope.cga
*/


import volume: "volume.cga"
import facade: "facade.cga"

Complete_envelope -->
    volume.Create_volume

 

This means that the ‘Complete_envelope’ shape is passed on to the volume.cga and the ‘Create_volume‘ is applied to it. As mentioned, the volume rule is a subtractive transformation of the mass of the envelope that refines the outer shape, so that the building remains within the boundary of the desired development. Here’s our volume.cga rule.

 

/**
* File: volume.cga
*/


version "2019.0"

attr height_ground_floor= 5.5
attr height_floor = 3.1
attr slab_thickness = 0.4

Create_volume -->
    split(y){height_ground_floor: Ground_floor
            |{~height_floor     : Floor }*}

#--- Ground floor
Ground_floor -->
    split(y){~1            : Ground_floor_volume
            |slab_thickness: Slab}

Ground_floor_volume -->
    comp(f){bottom: Bottom_srfc_ground}

Bottom_srfc_ground -->
    offset(-4)
    comp(f){inside: Offseted_ground
           |border: color("#4A966C") End.}

Offseted_ground -->
    reverseNormals
    extrude(height_ground_floor - slab_thickness)
    comp(f){side: Enclosure_ground_floor}

##--- All other floors
Floor -->
    split(y){~1            : Floor_volume
            |slab_thickness: Slab}

Floor_volume -->
    comp(f){bottom: Floor_srfc}

Floor_srfc -->
    offset(-2)
    comp(f){inside: Offseted_floor
           |border: NIL}

Offseted_floor -->
    reverseNormals
    extrude(height_floor - slab_thickness)
    comp(f){side: Enclosure_floor}

 

The next step is to call the end shapes of the volume to the façade rule for further processing. This will be done again in the file envelope.cga, as this is our main rule set that manages all the other rules. To do that we need to add the following lines of code to envelope.cga:

/**
* File: envelope.cga
*/


volume.Slab -->
    facade.Slab

volume.Enclosure_ground_floor -->
    facade.Enclosure_ground_floor

volume.Enclosure_floor -->
    facade.Enclosure_floor

And finally, we can write our facade.cga that adds detail on the shapes that are outputted by the volume.

/**
* File: facade.cga
*/


version "2019.0"

Slab -->
    color(0.5,0.5,0.5) Slab_End.

Enclosure_floor --> #recursive subdivision of enclosure surface to walls + openings
    split(x){rand(0.5,3): Wall
            |rand(1,5)  : split(x){~1.3: Opening((split.index+1)/split.total)}*
            |~1: For_further_subdivision}

For_further_subdivision -->
    case geometry.area < 9: Wall
    else                  : Enclosure_floor

Enclosure_ground_floor -->
    split(x){~1.3: Opening((split.index+1)/split.total)}*

#Refine Wall
Wall -->
    extrude(-0.3) color("#FFFFFF") End.

#Refine Opening
Opening(n) -->
    split(y){{0.1: Mullion
             |~2: Glazing(n)}*
            |0.1: Mullion}

Glazing(n) -->
    case n == 1: #last glass panel, mullion in both sides
        split(x){0.11: Mullion
                |~1  : Glazing_material
                |0.11: Mullion}
    else: #all other panels, mullion only in one side
        split(x){0.11: Mullion
                |~1  : Glazing_material}

Mullion -->
    color(0.4, 0.4, 0.4)
    extrude(0.05)
    Mullion_end.

Glazing_material -->
    set(material.color.r, 0.5)
    set(material.color.g, 0.8)
    set(material.color.b, 1)
    set(material.opacity, 0.6)

  

 

Once we’ve started creating detailed buildings it becomes useful to be able to turn on and off features according to the task at hand, so that we can always work on the desired level of abstraction and avoid waiting for the generation of unnecessary details. To do that, we add some Boolean parameters to envelope.cga that will be used in conditional statements to decide if the shapes are called or not by the sub-rules.

/**
* File: envelope.cga
*/


@Order(1)@Enum(true, false)
attr create_volume = true

@Order(2)@Enum(true, false)
attr create_facade = true

#volume          
Complete_envelope -->
    case create_volume == true:
        volume.Create_volume
    else:
        Envelope_end.

#facade
volume.Slab -->
    case create_facade == true:
        facade.Slab
    else:
        Slab_end.

volume.Enclosure_ground_floor -->
    case create_facade == true:
        facade.Enclosure_ground_floor
    else:
        Ground_floor_end.

volume.Enclosure_floor -->
    case create_facade == true:
        facade.Enclosure_floor
    else:
        Floor_end.

Once we select the building in the inspector window we see all the parameters that we have set, and there we can manually change the attributes. Note that if we select more than one buildings that have the same parameters, then we can change them for all the buildings with one move. A handy tip for selecting all the buildings that are generated by the same rule is to right click on one building and then click on 'Select Objects with Same Rule File'.

 

 

We can continue adding rules that are controlled by the envelope, for example ground floor plan, interior plan, landscape, green space, rules that add furniture and so on. Also, we can create variations of envelope, volume or façade rules with the same inputs and outputs and try different combinations of the code blocks. For example, if we create a second volume rule, we can add on the envelope.cga a parameter (volume_type) and a conditional statement that allows us to change the volume rule, while the envelope and the façade remain the same. Here's an example of an alternative volume rule with the same envelope and façade rules.

 

/**
* File: envelope.cga
*/


@Order(3)@Enum("Type1", "Type2")
attr volume_type = "Type1"

Complete_envelope -->
    case create_volume == true:
        case volume_type == "Type1":
            volume.Create_volume
        else:
            volume.Create_volume_2
    else: Envelope_end.

 

 


Level of detail (LOD)

 

It is a good practice to keep the polygon count (i.e. the detail) of the shapes as low as possible, especially if they are repeated many times over the scene, because that can lead to a heavy scene that can be difficult to navigate or export. To see the number of polygons of one or more shapes, press ‘d’ twice while having the viewport active. On the bottom of the viewport you can see the total number of polygons for the selected shapes. However, for many applications, such as viewing a scene in virtual reality (VR), there is the need to create highly detailed buildings that have all the necessary information for close views. As a result, it is useful to be able to control the level of detail (LOD) of the shapes, so that we can decide how much complexity we want in each shape depending on how close they are to our point of interest or cameras. In other words, we can create alternative options for the generation of geometry depending on how detailed we need each building to be. Firstly, we add a parameter in the envelope.cga that controls the level of detail.

 

/**
* File: envelope.cga
*/


@Enum("High", "Low")
attr level_of_detail = "High"

In which sub-rule/rules alternative options need to be created for the generation of geometry depends on the characteristics of each design. In our example the detail is added on the façade rule, so there we will provide an alternative option of how the openings can be generated when less detail is required. On the facade.cga we create the same parameter.

/**
* File: facade.cga
*/


@Enum("High", "Low")
attr level_of_detail = "High"

As the import of the façade in the envelope is left to default, attribute values from the importing rule file (envelope.cga in this case) are propagated to the imported rule file (facade.cga). This means that any change we make on the parameter level_of_detail in envelope.cga automatically overwrites the value of the same parameter in facade.cga. (Find more information about the import of CGA rules in the CityEngine documentation). Then we add a low detail option for the openings to  facade.cga:

/**
* File: facade.cga
*/


Glazing(n) -->
    case level_of_detail == "High":
        case n == 1: #last glass panel, mullion in both sides
            split(x){0.11: Mullion
                    |~1  : Glazing_material
                    |0.11: Mullion}
        else: #all other panels, mullion only in one side
            split(x){0.11: Mullion
                    |~1  : Glazing_material}
    else:
        case n == 1: #last glass panel, mullion in both sides
            split(x){0.11: Mullion
                    |~1  : Glazing_material
                    |0.11: Mullion}
        else: #all other panels, mullion only in one side
            split(x){0.11: Mullion
                    |~1  : Glazing_material}

Mullion -->
    case level_of_detail == "High":
        color(0.4, 0.4, 0.4)
        extrude(0.05)
        Mullion_end.
    else:
        color(0.4, 0.4, 0.4)
        Mullion_end.

High LOD, 15.400 polygons (left), low LOD 3.065 polygons (right)

 

Now by selecting the building and changing the level_of_detail parameter on the envelope.cga we see a substantial difference in the polygon count, in this example approximately 15.000 versus 3.000 polygons.

 

Reporting and view analysis tools

 

 

By bringing everything together, we now have created a custom set of procedural tools for generating buildings and public spaces specific to our project. During the formation of this computational basis for the project a series of design decisions had to be made regarding the organization of the rules and their content. At the same time, many more design decisions are required for the completion of the project in order to specify how to use these rules in a reasonable and consistent way to achieve the design goals. This process is facilitated by a range of analysis tools offered in CityEngine. A very useful tool for analysis is the reporting feature of CityEngine that allows to extract quantified information from the models in order to evaluate the goals that are achieved. Another example of analysis tools are the visibility analysis tools offered in CityEngine that compute in real time surfaces that are visible or hidden to an observer with a given position and width of view.

 

ESRI.lib: a ready-to-use procedural modeling library

 

CityEngine includes a library of rules and assets called ESRI.lib. The library can be used directly from CGA rules and can be customized according to specific project needs. The library includes assets like ground cover, plants and street furniture, as well as CGA rules for buildings, façades, streets and plant distribution. To install and manage ESRI.lib in CityEngine go to File > Manage ESRI.lib.

 

An example of a handy feature in ESRI.lib is the library of plants (82 species) that are available in different representations, as well as a rule for distributing them with various mixture and density options (Plant_Loader.cga, Plant_Distributor.cga).

 

 

We can import and use those in our rules in the same way that we imported and used multiple rules in the envelope.cga at the previous example. For example, once we have downloaded ESRI.lib and want to connect our rule to the Plant_Distributor.cga then we can import it in our rule and then we can set its parameters and use its functionalities.

 

# import Plant_Distributor from ESRI.lib
import Plant_Distributor: "/ESRI.lib/rules/Plants/Plant_Distributor.cga"

Create_garden -->
    set(Plant_Distributor.Density, 1)
    set(Plant_Distributor.Mix , "Random Shrub")
    Plant_Distributor.Generate

 

CityEngine tutorials and examples 

 

Comprehensive tutorial and example projects are also available for download in CityEngine. They are useful for learning how to use CityEngine and they can also be used as a source of ready-to-use rules and assets. To download them, go to Help > Download Tutorials and Examples. For example, in the Example_Complete_Streets__2016_0 you can find a collection of rules for creating streets along with textures, street furniture, street lights, cars and other useful assets.

 

 

 

 

 

 

 

Visualization strategies with Unreal Engine

 

 

The possibility to export CityEngine projects to Unreal Studio, opens new possibilities for design. Thanks to the support for Datasmith files in both sides, this process becomes quick and simple. This ease allows to establish a bidirectional relation between generative design and visualization/animation that can inform each other through a series of design iterations and back and forth from CityEngine to Unreal Engine. Two templates exist on the Unreal Engine Marketplace for importing CityEngine projects, the CityEngineModelLoader that is for simple import, and the CityEngineVRExperience is set up for VR visualization.  They have the basic light setup and other useful functionalities tuned for visualizing large scale projects.

 

> Documentation of CityEngine to Unreal Studio Workflows     

 

Other useful resources:

> ArcGIS Blog post: CityEngine VR Experience

> Webinar: Unreal and CityEngine: The future of urban design visualization

> Getting started guide: CityEngine VR Experience for Unreal Studio 

 

 

Default export 

 

The export-import process includes the following steps:

  • Select the shapes that you want to export in CityEngine.
  • Click on File > Export Models > Unreal Engine. The following export dialog comes up:

 

  • Select name and location of file and set the parameters as in the screenshot and click on Finish.
  • Additional options: for customizing the export further and using other mesh merging and instancing parameters refer to the CityEngine Datasmith export manual.

 

In Unreal Engine, then open the CityEngine Model Loader or the CityEngine VR Experience template iand click on Import Datasmith leaving the default values on the window that comes up.

 

Materials

 

Unreal Engine 4 uses a physically based material and shading model that approximates the light’s behavior leading to more accurate and natural looking results in relation to other rendering techniques. The physically based rendering (PBR) materials have properties such as base color, roughness, metallic and specular that can be set either to values (from 0 to 1) or to texture maps. Although CityEngine does not render PBR materials, it offers the possibility to set all the necessary material properties in the CGA rules during the procedural generation of shapes. Then, during the import of the Datasmith file in the templates, PBR materials are automatically created as instances of the master materials that exist in the templates, maintaining all the properties that were set in CityEngine. This facilitates significantly the export-import process and allows for quick and easy visualization of textured CityEngine scenes.

 

> More information on Unreal Engine 4 shading model and materials 

 

Setting material properties in CityEngine

 

A category of the attributes of shapes in CityEngine are the material attributes that control the shading and texturing of the shape's geometry. All of these attributes can be defined using the set shape operation. More specifically, for PBR material shaders, CityEngine supports the following attributes: emissivemap, occlusionmap, roughnessmap, metallicmap, emissive, metallic, roughness, color, colormap, opacity, opacitymap and normalmap.

 

> Material attributes in CityEngine

> Texturing in CityEngine Tutorial

 

Properly setting the desired materials properties, textures and UV coordinates in CityEngine is necessary to ensure a smooth process. So, let’s take a look at how to do that. Firstly we need to set the shader name to "CityEnginePBRShader", and then set all the necessary material properties. Here's an example for the creation of metal (Titanium), using the measured basecolor values from the Unreal Engine documentation.

 

version "2019.0"

Metal_titanium -->      
     set(material.shader, "CityEnginePBRShader")
     set(material.color.r, 0.542)
     set(material.color.g, 0.497)
     set(material.color.b, 0.449)
     set(material.opacity, 1)
     set(material.metallic,1)
     set(material.roughness,0.2)

Resulting material in Unreal Engine's material editor

 

If we want to use textures / maps, then we need to follow the steps of texturing (setupProjection(), projectUV()) along with setting the material attributes. Here's an example for texturing a concrete wall starting from an extrusion shape, using the following textures.

 

   

                                 color map                                                                    metallic map                                                                         normal map   

 

 

                          occlusion map                                                             roughness map

 

version "2019.0"
attr concrete_tile = 2.5

Shape -->
    set(material.shader, "CityEnginePBRShader")
    extrude(2)
    comp(f){all: Concrete}

Concrete -->
    setupProjection(0,scope.xy, concrete_tile, concrete_tile)
    projectUV(0)
    set(material.roughness,1.0)
    set(material.metallic,1.0)
    set(material.colormap,"concrete_material_textures/Color.jpg")
    set(material.metallicmap, "concrete_material_textures/Metallic.jpg")
    set(material.roughnessmap, "concrete_material_textures/Roughness.jpg")
    set(material.normalmap, "concrete_material_textures/Normal.jpg")
    set(material.occlusionmap, "concrete_material_textures/Occlusion.png")

Resulting material in Unreal Engine's material editor

 

Material replacement

 

After import in Unreal Engine, the materials can be manually replaced by dragging and dropping a different material on the material slots of the static meshes. While manual replacement is useful for quickly testing out alternatives, in the large scale it can be impractical. To increase the flexibility of the workflow, CityEngine materials can be automatically replaced upon import with Unreal Engine materials. In order to achieve this, we can assign Unreal Engine materials directly in the CGA rules as placeholders, so they get automatically replaced upon import. This is done by setting the material.shader to point to the desired material:

set(material.shader,/Game/Path_to_material_in_Content_folder/Name_of_material”)

To get the path information of a material in Unreal Engine, right click on the material asset and click Copy Reference in the menu. The information that is copied in the clipboard includes the path to the material that we need to set the material.shader. For example, in the following material reference the path that is needed for the replacement is bold and underlined.

 

Material'/Game/Materials/CityEngineMaterials/MyMaterial.MyMaterial'

 

Creating high quality PBR materials can be a challenge. There are various tutorials, libraries and plugins that offer help. A good starting point is the Substance plugin for Unreal Studio that offers a variety of materials and techniques. There are also various free libraries of PBR materials. 

 

> Detailed documentation on template materials

 

> Substance plugin

> Share Substance 3D

> FreeBPR

 

 

Actor replacement

 

A design project at urban scale can require distribution of large number of actors (e.g. for foliage) throughout the scene, which can be time consuming if done manually. To facilitate this task, we have created an Unreal Engine tool, built into the CityEngine VR Experience template, that helps with replacing actors. You can access it by clicking on CityEngine Toolbox > Replacement Tool (Beta).

 

In CityEngine we can use CGA rules to prepare these actors by using placeholders. The placeholders that bear information of the actor’s name, position and scale, can be exported from CityEngine in large numbers as instances, and exchanged in Unreal Engine with assets using the replacement tool. Using that strategy, in our project we replaced more than 100.000 placeholders to foliage or other static meshes such as doors, windows, and street furniture.

 

> More information on the replacement tool in CityEngine to Unreal Studio Workflows: Batch-Replacing Actors and Foliage (VR Experience Only)

 

To illustrate the overall process, let’s look at a simple example of a garden for how to distribute and replace placeholders. In CityEngine, we need to prepare placeholders with the following information: position; orientation; scale; and id of the desired asset. The position, rotation and scale are conveyed in the geometrical characteristics of the placeholder, while the id is conveyed in its name, that comes from the name of the asset.

 

Note that sometimes it is not necessary to specify the scale, because the size of the placeholder that we use is not important (for example when we replace foliage actors). However, in other cases (for example when replacing building components such as doors and windows) the scale is important, and we need to make sure that our placeholder has the right size. In this example we demonstrate the replacement of foliage for a garden, so the scale does not concern us.

 

Creation of the placeholder

For our placeholders we created a plane in Rhino saved as an Alias Wavefront OBJ file. Note that the origin of axes during the creation of the asset is important, as the replacement in Unreal Engine will be positioned at that origin regardless of where the geometry is. For that reason, it is good practice to have the origin of axes in the center of the geometry. The OBJ file is placed in the assets folder of the CityEngine project.

 

Distribution of placeholders in the CGA rules

Now, we want to insert a) small plants and b) big trees in the scene. We create two placeholders (with identical geometry, but different names), and place them in our assets folder. Our folder structure looks as follows:

 

 

In addition, we write an example rule that makes garden tiles like in this image:

version "2019.0"
attr extrusion_width = 0.2

Lot -->
    setupProjection(0, scope.xz, 3, 3)
    projectUV(0)
    split(x){~7: split(z){~7: Tiles}*}*

Tiles -->
    25% : Randomise_scope("G1") #full garden
    15% : Randomise_scope("G2") #stripes
    25% : Randomise_scope("G3") #plain floor
    else: Randomise_scope("G4") #half garden half floor

Randomise_scope(type) -->
    25% : rotateScope(0,90,0) Create_tile(type)
    25% : rotateScope(0,-90,0) Create_tile(type)
    25% : rotateScope(0,180,0) Create_tile(type)
    else: rotateScope(0,0,0) Create_tile(type)

Create_tile(type) -->
    case type == "G1": #full garden
        Green_area
    case type == "G2": #stripes
        split(x){~1.3 : Green_area
                |~0.6 : Floor}*
    case type == "G3": #plain floor
        Floor 
    else: #half garden half floor
        split(x){~1: Green_area
                |~1: Floor}

Green_area -->
    offset(-extrusion_width)
    comp(f){inside: Grass 
           |border: Border}

Border -->
    extrude(0.12)
    comp(f){all: setupProjection(0, scope.xy, 3, 3)
                 projectUV(0)
                 rotateUV(0, -90)
                 Floor}

We further assign Unreal Engine materials available from the free collection of materials offered by the Substance plugin, so that they are automatically replaced upon export, as discussed in the previous paragraphs. In order for the material replacement to work, the downloaded materials need to be placed in the path that is specified in the material.shader of the CGA rule

Floor --> 
    color("#C8CBCC")
    set(material.name, "Vertical_Tiles_MAT")
    set(material.shader, "/Game/Materials/CityEngineMaterials/Vertical_Tiles_MAT")

Grass -->
    color("#68A620")
    setupProjection(0, scope.xz, 2.5, 2.5)
    set(material.name, "Grass_Countryside_MAT")
    set(material.shader, "/Game/Materials/CityEngineMaterials/Grass_Countryside_MAT")

Now we want to distribute the placeholders in the green areas. We want to scatter small plants in all the green areas, and place a big tree in the center of each green area that has sufficient area for that. The scatter operation is an easy way distribute points in a uniform or Gaussian distribution type on a surface, while the split operation is useful when we want more control on the position (here for the big trees). Then the OBJs can be imported in CGA using the insert operation, which reads a geometry asset (3D model, polygon mesh) from a file and inserts it into the scope of the current shape. We modify the Green_area rule to insert the placeholders:

 

Green_area -->
    case geometry.area > 10: # small plants and big tree
        offset(-extrusion_width)
        comp(f){inside: Grass
                        Distribute_small_plants
                        Tree_position
               |border: Border}
    else:                    # only small plants  
        offset(-extrusion_width)
        comp(f){inside: Grass
                        Distribute_small_plants
               |border: Border}

 

And finally we specify the Distribute_small_plants and the Tree_position rules. 

attr density = 0.5

Distribute_small_plants  -->
    scatter (surface, geometry.area * density, uniform){Plant_position}
     
Plant_position -->
    primitiveQuad(0.2,0.2)     
    i("garden_plant.obj")

Tree_position -->
    split(x){~1 : NIL
            |0.3: split(y){~1 : NIL
                          |0.3: color(1,0,0)
                                alignScopeToAxes(y)
                                i("tree_position.obj")
                                Tree_pos.
                          |~1 : NIL}
            |~1 : NIL}

 

Export to Datasmith

During export to Unreal Engine, in Geometry settings > Instancing chose either Disabled or Use Instancing so that each placeholder is imported as a separate actor in the scene, named after the asset that created them (in this case the garden_plant.obj or the tree_position.obj). Here’s what the result of the import in Unreal Engine looks like:

 

 

Replacement of placeholders

To open the replacement tool, click on CityEngine Toolbox > Replacement Tool (remember that this is only available in the VR Experience template):

 

 

After selecting the menu item, a dialog with a number of options opens:

 

 

We replace our placeholders using the name as a filter criterion. The image shows assets downloaded from Epic Games' Open World Demo Collection:

 

 

 

Bringing the city to life

 

A great advantage of using a game engine is that it offers both spatial and temporal dimensions for design. As a result, by linking CityEngine and Unreal Engine we can explore temporal aspects of the city using procedural design tools side by side with Unreal Engine blueprints. This opens a lot of space for exploration for vehicle traffic and human movement simulation, or shadow studies to name a few.

 

Day-night cycle

 

The changing light conditions of an urban scene play a vital role in its atmosphere, and their integration in the design workflow has long been a target of architects and planners. The latest versions of Unreal Engine allow to compute the accurate position of the sun based on the location, date and time zone. Building up on the basic sun position calculator and sky sphere, we created an extension that controls the environment actors of the scene such as lights, sky sphere and fog, and allows to visualize the full day-night cycle, either as a time-lapse or by stopping in specific hours of the day. This comes with default ready-to-use parameters but it’s also fully customizable, in order to be able to achieve the desired atmosphere for each project. The CityEngine VR Experience also supports replacing the builtin day light system with light provided by trueSKY.

 

> More information on the Sun Position, CityEngine to Unreal Studio Workflows: Sun Position, Skylight and trueSKY 

 

 

 

 

Moving actors

 

Moving actors can create a lively urban scene, give a reliable reference of scale, as well as help in the simulation behaviors, flows or traffic. Here we share the results of our experimentation with moving people. For the animation of the figures, we used Mixamo, a free and user-friendly online platform that enables auto-rigging (i.e., automatic animation) of static figures and exports animated figures in FBX format, compatible for import into Unreal Engine. From a visualization point of view, we wanted to avoid over-detailed characters that can be distracting from the design and contrast with the aesthetics of the environment. As a result, we opted for ‘ghost’ representations of fully detailed figures, to achieve the desired realism without clashing with the overall atmosphere. This was achieved by applying to the animated characters a custom translucent material instead of the default skin and clothes, that can be adjusted from translucent to opaque to give the characters the desired visual importance.

 

 

 

 

 

Exploration of the project in Virtual Reality (VR) 

 

The possibility to explore the 3D city in a VR environment offers a lot of potential both for the creation of new design workflows and as a means to communicate a project with other people in an immersive experience. 

 

CityEngine VR Experience for Unreal Studio

 

To see a project in VR, we open the CityEngine VR Experience template and import the project. After the geometry has been imported and all the materials are in the scene, we need to click on CityEngine Toolbox > Create VR Materials.

 

 

Then we press Play and the VR experience begins in a virtual planning office with a table that has the 3D model of the city placed on top. While in the office, we can pan, rotate and scale the model as well as change the time of day and sun direction.

 

 

The experience continues with the possibility to immerse in the model either on the street level, or on top of a building, by pointing and teleporting to desired locations. Also, predefined points-of-interest can be set, and they appear as portals so that we can easily teleport there. While in the city we can explore the design by teleporting around, an experience similar to walking, as well as change the hour of day and light conditions. In addition, different scenarios can be switched and explored during the VR session.

 

 

 

Metadata

 

Metadata is information that describes geometric or non-geometric characteristics of specific objects, providing detail and analysis during design workflows. In CityEngine, metadata such as information about cost of elements, material, area, volume type and so on, can be embedded on the shapes during their generation. All reports and object attributes in CityEngine can be converted to metadata upon export, depending on the selection at the General Settings > Export Metadata of the export window.

 

Once the Datasmith file has been imported in Unreal Engine, the metadata of each shape can be found by selecting in the World Outliner the Shape_Root and then in the Details again the Shape_Root (note: not the Shape_Root(Instance)). The metadata information can also be displayed as a pop-up during play-time (option only supported for the CEModelLoader template for the moment). In order to see the pop-ups open the level ‘CEMetadataTemplateScene’ the CEModelLoader Template and that is in the Content folder.

 

 

Then we import the Datasmith files on that level and click on Play. Once we left click on a shape that has embedded metadata, a pop-up window comes up that displays the embedded information.

 

 

> More information on Metadata, CityEngine to Unreal Studio Workflows: Exporting CityEngine Metadata to Unreal Engine 4

Attachments

    Outcomes