[043] How to Build a 5-Story Procedural Building in Esri CityEngine
Integrating extrude, split, comp, and index commands to build complex architectural forms.
This tutorial focuses on a practical exercise to implement varying floor heights, facade protrusions based on even/odd indices, and side-wall component divisions within a single CGA rule workflow. By following these steps, you will learn how to handle multi-level logic and conditional modeling in CityEngine.
So far, we have covered extrude, split, comp, and index.
There are many valid ways to implement this logic, so treat this workflow as just one example.
Building Requirements and Modeling Goals
Let's assume there is a building with the following specifications:
- The building footprint is a rectangle with a width (front) of 40m and a depth (side) of 20m.
- The height is 20m.
-
It is a 5-story building in total.
- The 1st floor height is 5m, and there is a 4m wide entrance in the center of the front facade.
- The 2nd to 4th floors have a height of 3m each, and the front of each floor is divided into 10 segments.
- The even-numbered segments on the 2nd floor front protrude outward by 1m.
- The odd-numbered segments on the 3rd floor front protrude outward by 0.5m.
- The 4th floor front segments are split vertically into two equal parts.
- The 5th floor height is 6m; the front is not divided, but the left and right sides are each split into three segments of equal width.
Let's proceed through the coding step by step.
Step 1: Create the Base Building Mass
- Draw the footprint.
2. Assign a height of 20m.
Step 2: Split the Building into Floors
Using split(y) to Define Floor Heights
3. Split it into 5 floors: the 1st floor is a single 5m split, the 2nd to 4th floors are 3m repeating splits, and the 5th floor is a single 6m split.
3.1 For the 1st floor, use split(x) to create a fixed 4m wide entrance in the center.
3.2 Identify each floor by referring to the index value of the split(y) from line 7.
Step 3: Create the Second-Floor Facade Pattern
3.3 - Now, let's process them one by one starting from the 2nd floor.
Separating Building Faces with comp(f)
3.3.1 - Isolate the front facade of the 2nd floor.
3.3.2 - Divide the isolated front into 10 segments. Since the width is 40m, split it at approximately 4m intervals.
Creating Repetitive Architectural Elements
3.3.3 - Protrude the even-numbered segments by 1m by referring to the index value generated during the split.
Step 4: Create Alternating Protrusions on the Third Floor
3.4 - Now for the 3rd floor. The process is the same as the 2nd floor, but only the odd-numbered segments are protruded by 0.5m.
Step 5: Add Vertical Subdivisions to the Fourth Floor
3.5 - Moving on to the 4th floor. It is also divided into 10 segments, but each segment is split vertically into two parts.
Step 6: Split the Side Facades of the Top Floor
3.6 - For the 5th floor, leave the front facade as it is and split only the two side facades into three equal widths. This requires using different selectors in comp().
The final result is now complete. Pretty straightforward, right?
Complete CGA Code
The full code is as follows:
Lot --> extrude(20) A
A -->
split(y)
{5 : ground_floor |
# 1st Floor 5m
~1 : split(y){3 : mid_floor}* |
# Set middle floors as one area and repeat 3m splits within it
6 : top_floor}
# 5th floor is a single 6m split
ground_floor -->
split(x){~1 : wall. | 4 : door. | ~1 : wall.}
# Entrance is fixed at 4m width, others distributed equally as approximations
mid_floor -->
case split.index==0 :
second_floor
# Since the split(y) on line 9 starts from the bottom,
# index 0 corresponds to the 2nd floor
case split.index==1 :
# index 1 corresponds to the 3rd floor
third_floor
else :
# The rest is processed as the 4th floor
fourth_floor
second_floor -->
comp(f){front : second_front_wall | all : X.}
# Isolate only the front facade of the 2nd floor
second_front_wall -->
split(x){~4 : second_floor_window}*
# Repeatedly split the isolated 2nd-floor facade at ~4m intervals
# The ~ can be used or omitted
second_floor_window -->
case split.index%2==1 : extrude(1) X.
# When the index from the split on line 38 is divided by 2,
# Since split indexing starts at 0,
# a remainder of 1 corresponds to the 2nd, 4th, 6th... segments
else : X.
# Keep the remaining segments unchanged
third_floor -->
comp(f){front : third_front_wall | all : X.}
# Isolate the front facade of the 3rd floor separately
# Since split indexing starts at 0,
# a remainder of 0 corresponds to the 1st, 3rd, 5th... segments
third_front_wall -->
split(x){~4 : third_floor_window}*
# Repeatedly split the isolated 2nd-floor facade at ~4m intervals
third_floor_window -->
case split.index%2==0 : extrude(0.5) X.
# When the index from the split on line 55 is divided by 2,
# a remainder of 0 indicates an odd segment, so extrude 0.5m
else : X.
fourth_floor -->
comp(f){front : fourth_front_wall | all : X.}
# Isolate the 4th floor front
fourth_front_wall -->
split(x){~4 : fourth_floor_window}*
# Repeatedly split the isolated 2nd-floor facade at ~4m intervals
fourth_floor_window -->
split(y){~1 : X. | ~1 : X.}
# Split each partitioned window again vertically
top_floor -->
comp(f){
left : top_floor_window |
right : top_floor_window |
all : X.}
# Isolate the left and right sides of the 5th floor
top_floor_window -->
split(x){~1 : X. | ~1 : X. | ~1 : X.}
# Evenly split the isolated sides into a 1:1:1 ratio
Detailed CGA Code Breakdown and Logic
To help beginners fully grasp the procedural structure used in the complete code above, here is a detailed breakdown of the core logic and parameters implemented in each rule:
-
Vertical Floor Splitting (Lines 5-11): The
split(y)command breaks down the 20-meter total building height into three distinct zones. The ground floor is locked at a fixed 5 meters, the top floor is assigned 6 meters, and the remaining middle section utilizes a repeating 3-meter split ({3 : mid_floor}*). This repeating asterisk (*) syntax is highly efficient as it automatically loops the rule for the 2nd, 3rd, and 4th floors based on the total remaining mass height. -
Floor Identification via Split Index (Lines 16-25): Inside
the
mid_floorrule, CityEngine evaluates the conditionalcase split.indexstatement. Since vertical split indexing always starts from 0 at the baseline,index == 0accurately isolates the 2nd floor,index == 1targets the 3rd floor, and theelsefallback catch-all block handles the 4th floor seamlessly. -
Even and Odd Facade Protrusions (Lines 38-60): To create
the alternating architectural patterns, we apply a mathematical modulo
operation to the horizontal split indices. On the 2nd floor,
case split.index % 2 == 1filters out even-numbered segments (since indexing starts at 0, the 2nd, 4th, and 6th visual segments return a remainder of 1), triggering anextrude(1). On the 3rd floor, the inverse logiccase split.index % 2 == 0catches the odd-numbered segments, applying a subtleextrude(0.5)protrusion. -
Nested Subdivisions & Multi-Selector Component Splits (Lines
66-83):
The 4th floor grid is enhanced by nesting a vertical Y-axis split directly
inside each horizontal window segment, dividing them into equal halves to
mimic modern window panes. Finally, the 5th-floor rule showcases
multi-selector efficiency by combining
leftandrightinto a single line, allowing both side walls to simultaneously inherit a clean 1:1:1 proportional horizontal grid subdivision.
I have built this by separating it into individual rules step by step, using only the content we have learned so far.
There is no single correct answer in coding. Just think of it as "Oh, you can code it this way too."
Once you learn how to pass parameters to rules, use functions, use variables, and import rules, your code will become much more concise.
From the next lesson, we will cover scaling, moving, and rotating.
- Learn logical branching in [042] Understanding case...else Conditional Rules in CityEngine CGA
- Continue learning with [044] Understanding Scope and Object Movement in CityEngine CGA
It means learning to live through them without forgetting who you are."
Comments
Post a Comment
Feel free to leave a comment if you have any questions about Global Mapper or CityEngine. I will get back to you with a sincere response as soon as possible. (Please note that all comments are moderated and manually approved to maintain a high-quality community. Promotional or spam content will not be published.)