[006] Understanding the Basic Concepts of Procedural Modeling in CityEngine

Explore Model Hierarchy in CityEngine to Design Dynamic and Flexible Rule-Based Structures

In this tutorial, you will explore the fundamental differences between traditional 3D modeling and CityEngine's Procedural Modeling. By understanding the concept of Model Hierarchy, you will learn how to design flexible, rule-based structures that automatically adapt to changes in geometry and parameters.

Suppose you are modeling the building below using a traditional 3D program.

Building facade with repeating windows
figure 1 : Building facade with repeating windows

Traditional 3D programs (like 3ds Max or Blender) do not enforce a strict workflow order. You can create walls, windows, and floors separately and place them wherever indicated on the blueprint without any issues. There is no predetermined rule about what must be built first.

Using the building photo above as an example, traditional modeling would result in a structure like the diagram below.

Diagram of traditional 3D object hierarchy
figure 2 : Diagram of traditional 3D object hierarchy

In this scenario, each object has no relational connection with the others. They are simply fixed in their designated spatial coordinates.

What if you want to add another window to the 3rd floor? You would simply copy an existing window and place it, making sure it doesn't float outside the building. What if you want to add another floor? You stretch the wall polygons upward, copy a row of windows, and manually position them again.

A wall is just a wall, a door is just a door, and a window is just a window. Changes to one object do not affect the others. If the building's overall width changes, the windows won't automatically recalculate their spacing or count. If you reduce the number of floors, the windows floating in the removed space won't delete themselves.

Simply put, every object is completely independent.

💡 Practical Tip: I'm not saying this traditional workflow is inefficient. For custom, hero-asset modeling, it is necessary. I'm merely using it as a comparison to highlight the systemic nature of CityEngine's procedural generation.

The Concept of Procedural Modeling

So, what exactly is Procedural Modeling in CGA (Computer Generated Architecture)?

If we represent that same building using a procedural modeling workflow, the diagram looks entirely different, as shown below.

Diagram of procedural modeling hierarchy
figure 3 : Diagram of procedural modeling hierarchy

The objects aren't floating independently; they maintain a strict, nested hierarchy. Unlike independent objects, each element contained within the main structure (the wall/facade) can dynamically influence its lower-level components and reflect structural changes automatically.

Verifying the Hierarchy in CityEngine

Let's verify this by examining the hierarchy of a model generated entirely by CGA code.

Open CityEngine, create a new project, a new scene, and a new rule file. Set up your 3D View as shown below. Today, you can draw the building footprint slightly larger.

Setting up the CityEngine viewport
figure 4 : Setting up the CityEngine viewport

Please draw a rectangle footprint roughly 20m wide and 10m deep.

Drawing a 20m by 10m rectangle footprint
figure 5 : Drawing a 20m by 10m rectangle footprint

Now, apply the following CGA code. You do not need to type the comments (the text after `#`).


attr floor_Height = 3    # Initial floor height
attr floor_Count = 3     # Initial number of floors

Lot -->
    extrude(y, floor_Count * floor_Height) 
    # Extrude the footprint upwards by total height
    Facade

Facade -->
    split(y){ ~floor_Height: Floor(split.index) }*
    # Split the facade vertically by floor height 
    # and pass the index to the Floor rule

Floor(idx) -->
    case idx == 0:
        # Ground Floor: 3m wide windows repeating
        split(x){ ~1: Wall | 3: Window | ~1: Wall }*
    case idx < split.total - 1:
        # Middle Floors: 1m wide windows repeating
        split(x){ ~1: Wall | 1: Window | ~1: Wall }*
    else:
        # Top Floor: A large panoramic window taking 60% of the width
        split(x){ ~1: Wall | ~0.6: Window | ~1: Wall }

Wall -->
    color(0.5, 0.5, 0.5) # Gray walls

Window -->
    color(0, 0, 1)       # Blue windows

Don't obsess over the specific syntax of this code just yet; it's a rough draft designed strictly to demonstrate the concept of hierarchy.

If you apply this rule file to your drawn shape and it looks like the image below, you have succeeded.

Generated procedural building model
figure 6 : Generated procedural building model

In the Inspector panel, try changing the floor height and floor count to see if the building updates correctly.

Modifying attributes in the Inspector panel
figure 7 : Modifying attributes in the Inspector panel

Testing the Hierarchical Response

If the attributes apply correctly, let's observe what happens when the building's physical width changes.

Click the Scale button on the top toolbar (or press 'S') and select your object. You will see three axes inside the model. Grab the red cube (X-axis) and drag it left and right to stretch the building.

Scaling the building width using the Scale tool
figure 8 : Scaling the building width using the Scale tool

Notice what happens? As the width changes, the number of windows automatically increases or decreases on the lower floors, and the proportional width of the top-floor panoramic window adjusts dynamically.

This is the absolute advantage of procedural modeling: dependency allows higher-level spatial changes to automatically cascade down and re-evaluate lower-level components.

Exploring the Model Hierarchy Panel

Now, let's look beneath the hood and examine the exact hierarchical relationship of the object we just created.

From the top menu bar, select Window -> Model Hierarchy.

Opening the Model Hierarchy panel
figure 9 : Opening the Model Hierarchy panel

You will see the Model Hierarchy panel appear, usually next to the Inspector. Click the 'Inspect model' button at the top of this panel and click on your building model in the viewport.

Clicking the Inspect model button
figure 10 : Clicking the Inspect model button
Initial view of the model hierarchy
figure 11 : Initial view of the model hierarchy

Right-click anywhere inside the blank space of the Model Hierarchy panel and select 'Expand All'.

Selecting Expand All in the hierarchy panel
figure 12 : Selecting Expand All in the hierarchy panel

The entire tree structure will unfold, displaying the DNA of your procedural building.

Expanded view of the procedural model hierarchy
figure 13 : Expanded view of the procedural model hierarchy

Notice how perfectly this matches the theoretical diagram I showed you earlier? (If there are too many branches to see clearly, simply scale your building width down and check again). Each named Rule from our CGA code (Lot, Facade, Floor, Wall, Window) becomes a structural node in this tree.

Here is the brilliant part: if you click a specific rule in this tree structure, the corresponding geometry in the 3D View and the specific line in the CGA code editor will be highlighted simultaneously.

Conversely, if you select a specific window or wall directly on the 3D model, CityEngine traces it back, showing you exactly where that part exists within the hierarchy tree and which line of code generated it. This is how professionals debug, understand, and refine complex code.

💡 Practical Tip: When writing CGA code, you must visualize this process of progressive subdivision in your mind. You are coding a sequence: establishing the volume, dividing it into facades, slicing the facades into floors, and finally subdividing floors into walls and windows.

Limitations and Python Integration

This requirement for hierarchical thinking is exactly why I advise against jumping straight into writing code. If you want to model something, find a reference photo first. Practice sketching out the tree structure on paper—subdividing the asset step-by-step—before typing a single line. This planning makes coding exponentially easier.

Of course, CGA's procedural modeling has its limitations. Because it relies on top-down subdivision logic, a shape generally cannot access the information of a model generated from a different neighboring shape. Furthermore, you cannot natively pass information from a lower leaf node back up to a parent node.

A frequent question on the Esri Community forums is: "I want to determine the window shape of my current building by referencing the window shape of the adjacent model. How can I do this?"

The answer is always the same: "CGA cannot reference the attributes of other independent shapes natively. You must use a Python script."

CityEngine provides a powerful Python interface to globally access and manipulate shapes, models, and attributes across the entire scene.

Once you hit the boundaries of pure CGA, you will naturally find yourself needing to learn Python. For a glimpse into this, you can look forward to future lessons like [120] How to Apply Rules to Shapes Using Python Scripts in CityEngine.

Next Steps

Let's take a break from these heavy, headache-inducing theoretical concepts. Next, we will look at one of the most highly requested, practical out-of-the-box features in CityEngine.

In Lecture [007], we will cover Viewshed Creation (Visibility Analysis).

If you have any questions, please leave a comment. Let's solve them together!

"The truth doesn't matter. More accurately, our brains respond to the 'beliefs' we hold about a group, regardless of whether those beliefs are grounded in fact."
— Todd Rose, Collective Illusions
Continue Learning
Explore complete tutorial collections for CityEngine and Global Mapper.

Comments

Popular posts from this blog

029] How to Split Polygon Areas with Intersecting Lines in Global Mapper

[008] How to Understand CGA Rule Structure in CityEngine (Beginner Guide)

046] How to Create Parallel Offset Lines in Global Mapper