This article is the first of a series on procedural surfaces that will reveal the code of some of the chronotext experiments.

Here is a WebGL experiment showing what’s taking place in Sketch.h and Sketch.cpp from the SpiralLandscape project’s source folder. Follow the instructions in the chronotext-src repository to build and run the experiment on your computer.

If you run the experiment, you can see a landscape represented by one single continuous line. The height coordinates for the terrain are generated using Perlin Noise which can produce a unique configuration for each seed number.

Clicking on screen will change the seed number based on mouse coordinates, resulting in a new terrain generation.

main.cpp

On the desktop, we define a null screen-size, which will open a full-screen. As always, it’s possible to quit the sketch by pressing ESCAPE.

Sketch.h

We can see a Noise object and a custom Clock that will be used for animation.

Sketch.cpp

Sketch::Sketch()
:
noise(NOISE_SEED, NOISE_OCTAVES)
{}

In the sketch’s constructor, we initialize our Noise object with an arbitrary seed value and a number of octaves. The less octaves used the smoother the results will be. More octaves imply more computation.

Inside setup()

We call updateSpiral():

float l = TWO_PI * TURNS;
float L = PI * TURNS * (R1 + R2);
float dr = (R2 - R1) / l;
float DD = (DD2 - DD1) / l;
float D = 0;

do
{
  float r = sqrtf(R1 * R1 + 2 * dr * D);
  float d = (r - R1) / dr;
  D += DD1 + d * DD;

  float x = -sinf(d) * r;
  float y = +cosf(d) * r;

  lineBatch.addVertex(x, y, TERRAIN_H * noise.get(NOISE_SCALE * x, NOISE_SCALE * y));
}
while (D < L);

The spiral takes 2 radius values (R1 and R2) and the number of turns, plus 2 other parameters: DD1 and DD2, which correspond to the segment-length at R1 and R2 respectively (the beginning of the spiral has a higher curvature than the end, so it requires shorter segments.)

Then, we compute the 2d position of each point on the spiral, and we add a vertex corresponding to that point to lineBatch, but with some z coordinate obtained by multiplying the height of the terrain by the 2d noise value, which is between 0 and 1. Note that the x and y coordinates passed to the noise function must be scaled by some factor in order to produce satisfying results.

Inside draw()

auto now = animClock->getTime();
float azimuth = now * 2;
float distance = 250 - now * 3;

if (distance < 30)
{
  animClock->restart();
}

We want a rotation effect combined with a zoom-in. We can’t use the default clock provided by the sketch because at some point, we must restart the clock. Note that the clock we use here must be started (it takes place inside setup().)

The input functions

void Sketch::mouseDragged(int button, float x, float y)
{
  mousePressed(button, x, y);
}
void Sketch::mousePressed(int button, float x, float y)
{
  reseed(x * y);
}

Each time the mouse is pressed or dragged, we call reseed() by passing a seed number corresponding to the position on screen:

void Sketch::reseed(int64_t seed)
{
  noise.reseed(seed);
  updateSpiral();
}