This article shows how to create and customize procedural terrain objects in Archicad with GDL. It explains how to control terrain detail, generate repeatable or random‑looking landscapes, choose between mesh‑based terrain and NURBS‑based surfaces (curved, smooth shapes), and apply height‑based color gradients so elevation changes are easier to see. The examples are for users who already work with GDL and want to turn these techniques into GDL terrain objects that can be used in different projects.
Control terrain detail with octaves
Noise‑based terrain generation uses octaves to stack multiple noise layers on top of each other. As the number of octaves increases, the terrain becomes more detailed and visually interesting, but each additional octave adds another calculation loop, so generation time increases almost linearly and the benefits diminish after approximately 7 octaves.
Comparison of the effect of octave values on the generated result
Terrain detail increases with the number of octaves.
- Use lower octave counts for quick previews and testing.
- Increase octaves up to around seven for production to balance detail and speed.
-
Several octave settings can be compared side by side in order to select the most appropriate level of detail for a given project.
Use permutation tables for pseudo-random terrain
To generate repeatable pseudo-random terrain, GDL uses a permutation table as a lookup table for the noise function. In Perlin-style noise, this table stores a shuffled sequence of values that can be reused to produce consistent results.
Step 1: generate the permutation table in Python
You can generate the table in Python by creating the numbers 0 to 255 and shuffling them. If you use a fixed seed, you will always get the same permutation table, which is useful when you want repeatable terrain.
import random
random.seed(42) # Use any fixed value for repeatable results
permutation = list(range(256))
random.shuffle(permutation)
[234, 9, 103, 60, 5, 79, 232, 229, 45, 51, 131, 3, 168, 29, 170, 216,
99, 161, 111, 204, 220, 209, 78, 89, 72, 191, 157, 119, 226, 184, 244,
134, 21, 61, 175, 15, 223, 100, 230, 28, 128, 185, 84, 208, 164, 44,
113, 105, 27, 85, 203, 146, 153, 130, 66, 42, 250, 140, 174, 133, 115,
4, 52, 73, 65, 10, 104, 238, 30, 211, 46, 121, 2, 190, 159, 172, 112,
156, 95, 47, 124, 177, 77, 202, 81, 38, 123, 13, 182, 242, 64, 33, 225,
0, 241, 122, 210, 37, 106, 163, 82, 98, 34, 218, 187, 214, 125, 132,
120, 219, 252, 32, 135, 215, 245, 48, 198, 222, 76, 231, 213, 192, 227,
144, 19, 152, 110, 12, 217, 126, 196, 201, 248, 148, 109, 138, 63, 249,
200, 36, 197, 101, 127, 145, 149, 54, 16, 167, 102, 80, 239, 181, 14,
83, 224, 142, 69, 176, 118, 171, 251, 136, 43, 246, 155, 18, 165, 68,
53, 90, 94, 41, 93, 162, 116, 212, 205, 25, 235, 193, 74, 58, 169, 199,
17, 180, 49, 147, 92, 158, 160, 75, 141, 20, 96, 31, 137, 117, 186, 11,
67, 233, 88, 91, 24, 97, 237, 247, 86, 195, 236, 39, 221, 87, 240, 178,
40, 206, 194, 1, 207, 71, 150, 114, 56, 107, 243, 179, 166, 183, 50, 143,
254, 154, 129, 59, 55, 23, 7, 8, 108, 151, 22, 139, 228, 253, 173, 26,
188, 35, 255, 62, 70, 189, 6, 57]
Step 3: convert the Python list to a GDL-compatible array
Because GDL arrays use 1-based indexing, the Python list needs to be reformatted before pasting it into a GDL script. The following Python loop prints the shuffled values in a GDL-friendly format.
for index, value in enumerate(permutation):
print("permutation[{}] = {}".format(index + 1, value)) # GDL indexing starts at 1
Step 4: copy the final result into GDL
The output can now be copied directly into your GDL code as array assignments. Since the Python loop starts numbering at 1, the result matches GDL array indexing.
permutation[1] = 234
permutation[2] = 9
permutation[3] = 103
permutation[4] = 60
permutation[5] = 5
permutation[6] = 79
...
permutation[256] = 57
The permutation table is generated once in Python and then pasted into the GDL script as constant values. Because the randomization step happens only in Python and not in GDL, using this table will always produce the same height map each time the GDL object runs.
permutation[1] = 151
permutation[2] = 160
permutation[3] = 137
permutation[4] = 91
permutation[5] = 90
...
An alternate solution is to shuffle the permutation table in GDL:
! Generate a permutation table with the Fisher-Yates shuffle
! Initialize array
dim permutation[256]
_nPoints = 256
for dummy = 1 to _nPoints
permutation[dummy] = dummy
next dummy
! Perform Fisher-Yates shuffle on array
for shuffler = _nPoints to 1 step -1
j = 1 + int(rnd(shuffler - 1))
swapVal = permutation[shuffler]
permutation[shuffler] = permutation[j]
permutation[j] = swapVal
next shuffler
For cases where different terrain is needed each time, a simple shuffle algorithm can be used to randomize the lookup table inside the GDL script itself. The distribution is not perfectly even, but it is fast and accurate enough for terrain generation. This way, a new terrain is created from a random lookup table at each placement of the object. If the placed object is moved or rotated, the generated terrain remains unchanged.
Structure logic with GDL macros and subroutines
Macro‑like functions
In Python, functions are defined with a name, parameters, and a return value. In GDL there is no direct equivalent, so geometry is modularized by calling other library parts as macros. The macro’s name acts like a function name, and parameters are passed to control the generated geometry.
For the full syntax and options of the call command, see the GDL Center documentation on macro calls.
Macro calls are powerful because they let you package logic into separate objects and manage them as a reusable library. GDL also allows macros to be called recursively, which enables the creation of more complex procedural structures when required. When you want to distribute a coherent set of parts, formats like LCF make this workflow practical even for end users. At the same time, if your goal is a single, self‑contained object with no external dependencies, you may prefer to keep more logic inside one GDL script instead of splitting it into many macros. For this purpose – having reusable code lines inside a single GDL script – subroutines are the most fitting solution.
Subroutines
To keep everything inside one object and avoid external macros, you can structure your script into subroutines with gosub calls. In GDL, subroutines run in the same context as the calling script, so variables keep their values and can be read and written directly without any extra mechanism.
_result = 0 ! initialize variable (this line is not even necessary)
_value4 = 1
_value5 = 2
_value6 = 3
gosub "lerp"
! use _result for anything you need it for
end
"lerp":
_result = _value4 + _value5 + _value6
Apply bitwise operations in GDL
This subroutine computes the common bits of two input integers: it returns the value composed of the powers of two that both numbers share in their binary representation. Internally it uses BITTEST to inspect each bit and BITSET to build up the result.
value1 = integer1
value2 = integer2
gosub "bitand"
result = _commonBits
end
"bitand":
! Compute common bits (shared powers of two) of two integers.
! The result contains only those bits that are set in both value1 and value2.
bitlen = ceil(log(max(value1 + 1, value2 + 1)) / log(2)) ! Calculate the number of bits required to represent the bigger integer
_commonBits = 0 ! Initialize result variable
for bit = 0 to bitlen - 1
if bittest(value1, bit) & bittest(value2, bit) then
_commonBits = bitset(_commonBits, bit, 1) ! Turn on bit if it is found in both integers
endif
next bit
return
To perform the operation efficiently, the bit length of the larger input value is determined first to avoid unnecessary iterations. The bit values are then checked one by one, and when at least one of the bits at a given position is zero, the bit at that position in the target number is changed.
Bit Functions
BITTESTBITTEST(x, b)
Returns 1 if the b bit of x is set, 0 otherwise.
BITSETBITSET(x, b [, expr])
expr can be 0 or different; the default value is 1. Sets the b bit of x to 1 or 0 depending on the value of the specified expression, and returns the result. Parameter value should be integer, returned value is integer.
Animate objects with GLOB_FRAME_NR
Objects in Archicad are not moved continuously between frames. Instead, they are rebuilt for each frame of a fly‑through when animation is enabled in the Create Fly‑Through panel. The object cannot store state between frames, so all animation logic must be defined up front and driven by global variables. The main variable for this is GLOB_FRAME_NR, which stores the index of the current frame.
In a noise‑based terrain, the illusion of motion can be created by offsetting the noise field slightly for each frame based on GLOB_FRAME_NR. Because almost any parameter can be expressed as a function of the frame number, many different effects, from particles to surface deformation, can be animated.
Choose geometry method for terrain
There are several ways to generate terrain geometry in GDL. A simple mesh can be used, colorized triangle pairs can be built, or a smooth NURBS surface can be created. Each method has different trade‑offs in performance, visualization, and texture control.
Left to right: MESH with edges off, MESH with smoothed edges, colorized CSLAB_, merged triangles from PLANE_ with their joint edge smoothed, NURBS surface
Mesh‑based terrain
For many use cases, the simplest solution is to generate a MESH, which behaves in a similar way to the Mesh tool in Archicad. Grid sizes are provided in the x and y directions, and a z value is specified for each node.
Create colored terrain mesh
To build a colorized representation, the grid data is first reorganized and then used to generate the geometry.
-
Sort grid values into an array where each entry stores x,y,z for one point.
-
Loop through the array and, for each node, take the two triangles that form its quad.
-
For each triangle, compute a color value from the average z of its vertices.
-
Use CSLAB_ to generate the colored terrain:
-
CSLAB_ has simple inputs for the side faces.
- Different colors can be set for the top and bottom because the element is a fixed‑z extrusion.
-
- Use CUTPLANE to trim away any excess geometry.
-
To create a “quad” mesh instead of explicit triangles:
-
- Reuse the same sorted x,y,z array.
- Use PLANE_ instead of CSLAB_, and smooth the diagonal edge by applying the appropriate status code.
Generate NURBS surfaces
NURBS terrain is more complex but provides very smooth results and flexible texture mapping.
-
The point indexing over the entire grid is visualized, and the numbering of each point in both directions must be known.
-
When the NURBS surface is created, its edges are defined in an order that matches this point network.
-
Use the NURBS geometry commands to build the surface from those control points.
A NURBS surface has useful properties for terrain because its texture mapping is easy to deform, which allows textures to be warped in many ways, such as stylized height shading or flow lines.
Create a heightmap and color gradient with GDL
To create a height‑based color gradient on the terrain, lower areas are assigned one color and higher areas gradually blend toward another, with the gradient driven by each mesh plane’s z value. The following steps show how mesh becomes a heightmap, with geometry defined by the terrain heights and a color gradient that reveals elevation changes across the surface:
- Choose the colored mesh geometry type
In the object settings, the Geometry type is set to Colored Mesh. This setting instructs the object to store color information per mesh plane, not only raw geometry. - Define start and end colors
When the color array field appears, the RGB values for the start color and end color are entered.
-
The start color is applied at the lowest z value in the mesh.
-
The end color is applied at the highest z value.
-
- Map height to color in the script
In the GDL script, the z value of each mesh plane is read and remapped into the 0.0–1.0 range. That normalized value is then used to interpolate between the start and end RGB values, producing a smooth gradient from low to high areas. - Generate materials from RGB values
DEFINE MATERIALis used to create a material for each required color. For simple flat colors, the matte material type is used and the interpolated RGB values are passed as real numbers between 0.0 and 1.0 (0–255 values divided by 255). The material is then applied to the current mesh element withMATERIAL.
Archicad does have an upper limit on how many materials can exist in a project, but this limit is in the order of several thousand materials, so in practice it is rarely a constraint for this kind of procedurally generated terrain shading.
Example:
! Randomized grass material with slight variation in green tones
define material grass_rnd 2,
(76 + int(rnd(40))) / 255, ! red component: 76–115 (darker)
(110 + int(rnd(50))) / 255, ! green component: 110–159 (dominant)
(60 + int(rnd(30))) / 255 ! blue component: 60–89 (adds subtle variation)
material grass_rnd
In this example, the pattern uses RND to randomize each color channel while keeping values in the 0.0–1.0 range after dividing by 255, so each blade of grass gets a slightly different shade of green for a grass‑like material.