To access the tutorial projects in ArcGIS CityEngine, open CityEngine and click Help > Download Tutorials and Examples in the main menu. After choosing a tutorial or example, the project is automatically downloaded and added to your CityEngine workspace.
In this tutorial, you'll learn about mass modeling using L and U-shaped buildings, how to apply height and setback variation to parcels, and finally, generate a diverse looking scene including textures.
To learn more about CGA shape grammar, see the Rule-based modeling tutorial, and the Rule-based modeling and CGA modeling help topics.
Add L and U-shapes
First, you will create L and U mass models of buildings using CGA, which are common typologies for buildings in cities.
Create a rule file
To create a rule file, do the following:
- Expand the Tutorial_08_Mass_Modeling tutorial folder in the Navigator window.
- Open the 01_MassModeling.cej scene in the scenes folder.
Click No when asked "Would you like to regenerate these models?"
This is because you will apply rules and generate models later in the workflow.
- Click New > CityEngine > CGA Rule File.
- Click Next.
- Name the rule myMass_01.cga.
- Click Finish.
A new CGA file is created, and the CGA Editor window appears. It's empty except for the version number.
Add a L-shape
You'll start with a simple L-shape mass model.
- Add the following to the myMass_01.cga rule:
attr height = rand(30, 60) attr wingWidth = rand(10, 20) Lot --> LShape LShape --> shapeL(wingWidth, wingWidth) { shape : LFootprint } LFootprint --> extrude(height) Mass LotInner --> OpenSpace
The LShape rule creates an L-shape footprint, with dimensions ranging between 10 and 20 meters. The LFootprint rule extrudes the L-shape to its height.
LotInner applies OpenSpace, leaving the lot shape as is.
Next, you'll apply the rule to the lots. - Press Ctrl+S to save the rule file.
- For the moment, you won't need the streets to create models, so click the Visibility settings and deselect Graph Networks (F10) to hide the streets.
- To select all the lots, right-click the Blocks sublayer in the Streetnetwork layer in the Scene Editor window and click Select Objects:
- To assign the myMass_01.cga rule to the lots, do the following:
- Ensure the Lots tab in the Inspector window is selected.
- Next to Rule File, click Assign in the Inspector window.
- Click the myMass_01.cga rule in the Assign Rule File dialog box.
- Click the Select tool (Q) and select some lots in the Viewport window.
- Click Generate (Ctrl+G):
Showing just L-shaped mass models does work, but a mix of model shapes may be better. Next, you will add more variation to the mass models.
Add variation to the L-shape
The current L-shape's side wing is always positioned on the left side of the main mass. Using the rotateScope operation, you can change the L-shape to be on the right side as well.
- Change the LShape rule by making use of the % probability operator to improve the L-shape so that its side wing is on either the left or right side:
LShape --> 50% : shapeL(wingWidth, wingWidth) { shape : LFootprint } else : rotateScope(0, 90, 0) shapeL(wingWidth, wingWidth) { shape : LFootprint }
- Using the convexify operation, you can split the L-shape into its wings and change the height of the two wings. Again, you'll use random probability to add variation by editing the LFootprint rule:
LFootprint --> 75% : extrude(height) Mass else : convexify comp(f) { 0 : extrude(height) Mass | all : extrude(height*0.7) Mass }
- Save the rule file and generate:
Side wings now appear on the left and right sides and vary in height. Now, you'll add additional shape typologies.
Add a U-shape
To add a U-shape, do the following:
- Call the UShape rule instead of the LShape rule in the starting Lot rule:
Lot --> UShape
- Similarly, use the shapeU operation and add the UShape and UFootprint rules below the Lot rule:
UShape --> shapeU(wingWidth, wingWidth*0.7, wingWidth*0.7) { shape : UFootprint } UFootprint --> extrude(height) Mass
- Save and generate:
- Next, add some variation by randomly rotating some of the U-shapes 180-degrees:
UShape --> 80% : rotateScope(0, 180, 0) shapeU(wingWidth, wingWidth*0.7, wingWidth* 0.7) { shape : UFootprint } else : shapeU(wingWidth, wingWidth*0.7, wingWidth*0.7) { shape : UFootprint }
- Save and generate:
You can see that U-shapes do not work well on the all lots. In the next section, you'll combine the L and U-shapes.
L and U-shapes combined
Combine the L and U-shapes and add some variation in the height distribution.
- To have better control over the building heights, you'll add the condition to the height attribute that only large area lots can create tall buildings:
attr height = case geometry.area < 1000 : rand(20, 50) else : rand(50, 150)
- In the Lot rule, call LUShape instead of Ushape and add a new rule, LUShape, to control which shape typoloogy is created on which lots:
Lot --> LUShape LUShape --> case scope.sx > scope.sz : 60% : UShape else : LShape else : LShape
U-shapes work best on lots that are wider than they are deep, or translated in CGA grammar, in which scope.sx is larger than scope.sz. In all other cases, you'll trigger only L-shapes.
- Save and this time select more shapes to create more building masses.
- Generate the buildings:
- Neither L nor U-shapes work well on non-rectangular lots. The next case statement ensures the UShape and LShape models are only created on approximately rectangular lots (with a tolerance of 15 degrees). Otherwise, you'll call a new footprint rule:
LUShape --> case geometry.isRectangular(15) : case scope.sx > scope.sz : 60% : UShape else : LShape else : LShape else : BasicFootprint
- Since extruded lot shapes are too large compared to the L and U-shapes, add a BasicFootprint rule with a negative offset. This allows more space between individual buildings. Place it after the LFootprint rule:
BasicFootprint --> offset(-5, inside) extrude(height) Mass
- Save and generate:
To see what the scene looks like at this point, open the 01_MassModeling_.cej scene without saving. Click Yes when prompted to regenerate the models.
In the next section, you'll learn how to use recursion in the shape grammar for mass modeling.
Use recursion for mass modeling
Now, you'll model repetitive building elements using recursive shape grammar calls.
Tower shapes
- Create a new rule file called myMass_02.cga.
- Add the following to the new rule file:
height = case geometry.area > 1000 : rand(50, 200) else: rand(20, 50) Lot --> Tower Tower --> extrude(height) Envelope Envelope --> RecursiveSetbacks
The height function returns a random value for the building height. The Lot starting rule calls the Tower rule, which extrudes the footprint to the tower envelope. The Envelope rule calls the recursion rule.
- For the following recursive rules, you'll need three additional variables: lowHeight, scale, and floorheight and place them after the height function:
lowHeight = 50% : 0.4 else : 0.6 attr scale = rand(0.75, 0.9) attr floorheight = rand(4, 5)
Since, scale needs to be constant for a building, you'll define it as an attribute. This is because, unlike functions, attributes are only evaluated once at the beginning of the generation process.
- Add the RecursiveSetbacks rule after the Envelope rule:
RecursiveSetbacks --> case scope.sy > 2*floorheight : split(y) { 'lowHeight : Mass | ~1 : Setback } else : s('1, floorheight, '1) Mass
The RecursiveSetbacks rule splits the mass, as long as it's higher than two floors, into a lower part mass with the lowHeight relative height . The upper remaining part generates the Setback shape.
If the RecursiveSetbacks shape is smaller than two stories, the remaining part is set to floorheight as the height, and a Mass shape is generated.
- Add the Setback rule which scales and centers the shape and recursively invokes the RecursiveSetbacks rule:
Setback --> s('scale, '1, 'scale) center(xz) RecursiveSetbacks
- Save the rule file.
- Select all lots in the scene and assign the myMass_2.cga rule file in the Inspector window.
The simple building masses from the previous rule are changed to towers with setbacks:
Round shape
Using an external cylinder asset, you can create round versions of the recursive towers.
- Modify the Envelope rule:
Envelope --> case geometry.isRectangular(20) : 20% : i("cyl.obj") RecursiveSetbacks else : RecursiveSetbacks else : RecursiveSetbacks
In 20 percent of all towers, you'll insert a cylinder asset instead of using the implicit cube as the underlying shape:
- Save and generate:
The 02_MassModeling.cej scene shows what the scene should look like now.
In the next section, you'll modify the lot parcels with setbacks.
Adapt the parcel with setbacks
Apply setbacks to the lot shapes.
Street setback
To apply street setbacks, do the following:
- Create a new rule called myMass_03.cga.
- Add the following to the new rule file:
attr height = case geometry.area > 1000 : rand(50, 200) else : rand(20, 50) attr distanceStreet = 20% : 0 else : rand(3, 6) Lot --> Parcel LotInner --> OpenSpace Parcel --> setback(distanceStreet) { street.front : OpenSpace | remainder : Footprint } Footprint --> extrude(height) OpenSpace --> color("#77ff77")
The Parcel rule applies a setback on all the street sides of the lot and forwards this area to the OpenSpace rule. The inner part away from street sides, is forwarded to the Footprint rule which extrudes to a random height.
The street.front selector evaluates the streetWidth shape object attributes, which is automatically set for dynamic lot shapes created from blocks. These attributes may not be present on manually created shapes.
- Save the rule and select all lots.
- Click Shapes > Delete Models in the main menu to delete all the generated models.
- Assign the myMass_03.cga rule to all lots.
- Select just a single lot and generate:You can see the setback from the street is applied to the selected lot.
- Select some additional lots and generate again:
Notice how the buildings are setback from the street but there isn't open space between them.
Buildings distance
You’ll now add a similar setback to control the distance between the buildings.
- Add the distanceBuildings attribute below the distanceStreet attribute:
attr distanceBuildings = 30% : 0 else : rand(4, 8)
- Modify the Parcel rule to call the SubParcel rule in the remainder, and add the new SubParcel rule after it:
Parcel --> setback(distanceStreet) { street.front : OpenSpace | remainder : SubParcel } SubParcel --> setback(distanceBuildings/2) { !street.front : OpenSpace | remainder : Footprint }
In the SubParcel rule you again apply a setback, but this time on the non-street facing edges.
- Save and generate.
- Select a model to explore in the Inspector window:
The distanceBuildings and distanceStreet are rule parameters which you can adjust to change the setback distances. You can set the values on a single or multiple models.
To reset to the default random value from the rule file, click the drop-down menu next to either parameter value and click Rule default.
- Select all lots and generate:
You can open the 03_MassModeling.cej scene, if you want to see an example of the buildings with the setback parcels.
In the next section, you'll combine the mass models from the previous sections with the setback parcels.
Combine masses and setback parcels
Next, you'll combine mass models and the setback parcels.
Import LU shapes and tower mass rules
- Ensure that the myMass_03.cga window is active and save it as myMass_04.cga.
- Add the following two imports to the top of the rule file after the version statement:
import lushapes : "myMass_01.cga" import towers : "myMass_02.cga"
All rules, attributes, and functions from the imported rule file are now available for use in the current rule file.
- Modify the Footprint rule:
Footprint --> case geometry.isRectangular(15): 25% : towers.Tower else : lushapes.LUShape else: 25% : towers.Tower else : lushapes.BasicFootprint
Instead of a simple extrusion, the Footprint rule now generates the more advanced mass model you created in the previous steps. The geometry.isRectangular function in the case statement makes sure that the LUShapes are only generated on mostly rectangular shapes.
- Save the rule file.
- Select all lots and assign the myMass_04.cga rule file:
You can see all the lots with setbacks and a mixture of square and round towers with different heights.
The 04_MassModeling.cej scene shows the buildings and towers with setbacks.
In the next section, you'll add textured facades to the mass models.
Add textured facades
To add textured facades, do the following:
- Save the myMass_04.cga rule file as myMass_05.cga.
- Add a const function below the attributes that randomly selects one of your 12 facade texture tiles:
const randomFacadeTexture = fileRandom("*facade_textures/f*.tif")
- Next, add the floorHeight attribute:
attr floorheight = rand(4,5)
- To correctly map the texture tiles to the facade, define two functions that calculate the actual floor height and tile width of the current mass:
actualFloorHeight = case scope.sy >= floorheight : scope.sy/rint(scope.sy/floorheight) else : scope.sy actualTileWidth = case scope.sx >= 2 : scope.sx/rint(scope.sx/4) else : scope.sx
With these functions, you ensure that texture tiles are not cut off at the edge of the facade.
- Add a Mass rule at the bottom of the rule file using the component split to get the facade components from the mass models:
Mass --> comp(f){ side : Facade | top : Roof. }
- Next, instruct the imported rules to use this Mass rule:
towers.Mass --> Mass lushapes.Mass --> Mass
- Finally, in the Facade rule, setup the UV coordinates on the facade based on the actual, define the texture file using the randomFacadeTexture function, and project the UVs:
Facade --> setupProjection(0, scope.xy, 8*actualTileWidth, 8*actualFloorHeight) texture(randomFacadeTexture) projectUV(0)
- Save the rule file.
- Select all the lots and assign the myMass_05.cga rule to the lots.
- Generate the buildings.
The models now have randomized textured facades.
Open the 05_MassModeling.cej scene to see the final textured buildings.
In this tutorial, you learned how to do the following:
- Generate mass modeling using L and U-shaped buildings.
- Apply distance and setback variation to parcels.
- Create buildings with a diversity of shapes, heights, and textures.
To continue your learning with CityEngine, see the complete CityEngine tutorial catalog.