# vectordesert

![Bombing the range...lightly.](images/vectordesert-screenshot.png)

An infinite wire-mesh vector desert flythrough, written in C with OpenGL
(GLFW). A vast, mostly-flat wireframe plain rolls past, interrupted now and
then by vector mountain ranges, with saguaro cacti scattered across both.
The terrain is built from **square wire-mesh frames**; the saguaros are
ribbed wire-mesh tubes shaped to resemble real cacti (tapered, dome-tipped
trunks with arms that curve out and sweep upward).

## Build

Requires a C compiler, GLFW 3, and OpenGL.

Debian/Ubuntu:
```
sudo apt install build-essential libglfw3-dev
```
Fedora:
```
sudo dnf install glfw-devel
```
macOS (Homebrew):
```
brew install glfw
```

Then:
```
make
```

## Run

```
./vectordesert                 # windowed
./vectordesert --fullscreen    # full screen
```

### Command-line settings
```
--terrain-hue=0..1        hue of the terrain mesh         (default 0.075)
--mountain-freq=0.2..4    how often the plain is interrupted by a range
                          (low = rare; high = frequent)   (default 1.0)
--mountain-rough=0..1     flat-topped mesas .. jagged peaks    (default 0.5)
--mountain-min-height=0..80 height of a range's low parts     (default 6)
--mountain-max-height=0..80 height of the tallest peaks        (default 26)
--cactus-freq=0..1        chance of a cactus per cell      (default 0.35)
--cactus-size-var=0..0.95 random cactus size spread        (default 0.55)
--cactus-min-size=1..30   smallest cactus height           (default 4)
--cactus-max-size=1..30   largest cactus height            (default 12)
--cactus-hue=0..1         hue of the cacti                 (default 0.33)
--max-arms=0..12          arms on the largest cacti        (default 5)
```
Example:
```
./vectordesert --fullscreen --terrain-hue=0.55 --mountain-freq=1.8 \
               --cactus-hue=0.30 --cactus-freq=0.5 --max-arms=8
```

### Camera
| keys | does |
|------|------|
| `W` / `S` | throttle: accelerate / decelerate forward speed (holds when released; `S` past zero goes into reverse) |
| `X` | full stop (zero the camera's velocity) |
| `A` / `D` | strafe left / right |
| `Page Up` / `Page Down` | raise / lower camera altitude |
| `←` / `→` | pan view left / right |
| `↑` / `↓` | pan view up / down |
| `[` / `]` | decrease / increase rendering distance |
| `G` | toggle the floor-altitude indicator |
| `Space` | drop a bomb |

Velocity is held in world space, so panning the view with the arrow keys
only changes where you look — it does not change the direction you are
travelling. To steer, turn and then apply throttle.

The terrain is generated around the camera and cached — rebuilt only as you
travel out of the cached region — so the plain extends infinitely in every
direction. Rendering distance (`[` / `]`) ranges from 40 up to 960 units.

### Live setting controls
| keys | adjusts |
|------|---------|
| `1` / `2` | terrain hue |
| `3` / `4` | mountain range frequency |
| `,` / `.` | mountain roughness (flat mesas ↔ jagged peaks) |
| `T` / `Y` | minimum mountain range height |
| `U` / `I` | maximum mountain range height |
| `5` / `6` | cactus frequency |
| `7` / `8` | cactus size variation |
| `J` / `K` | minimum cactus size |
| `N` / `M` | maximum cactus size |
| `9` / `0` | cactus hue |
| `-` / `=` | maximum cactus arms |
| `F` | toggle fullscreen |
| `ESC` | quit |

Current settings are printed to the terminal as you change them, and an
on-screen heads-up display (drawn in the same vector style) lists every
setting next to the key(s) that change it. The HUD fades out after 10
seconds with no keypresses and snaps back as soon as you press a key.

All settings (and the rendering distance) are saved to `~/.vectordesert.cfg`
on exit and reloaded at startup, so the program resumes with the values from
your last session. Command-line options still override the saved values for
that run.

## Bombs

Press `Space` to drop a bomb. It falls from the sky onto a random spot in
your field of view, between half the rendering distance and the full
rendering distance away. When it strikes the ground it detonates into an
animated, all-vector mushroom cloud twenty times the height of the tallest
mountains, in an extremely bright random hue (additively blended so it
glows):

- an initial blinding fireball,
- a rising stem with climbing vortex rings,
- a billowing cap that stays joined to the stem — a rolling vortex torus
  whose outer edge curls up and over — and keeps animating as the whole
  cloud fades away.

The blast **obliterates every mountain and cactus within the cloud's total
radius** and **gouges a deep crater** — a steep-walled bowl with a raised
rim thrown up above the surrounding surface — at the impact point. Every
bomb leaves its own permanent crater, and craters never erase one another:
repeated blasts in the same place **accumulate**, deepening and reshaping
the crater. Drop as many as you like; the clouds animate and dissipate
independently.

## Notes

- **Arms scale with size:** the number of arms is derived from a cactus's
  normalized size, so the smallest cacti always have the fewest arms (down
  to a bare trunk) and the largest always reach `max-arms`.
- The world is generated procedurally from a hash-based value-noise height
  field, so it is effectively infinite — the camera flies forever. As well
  as mountain ranges rising above the plain, the terrain carves valleys and
  basins below it (and bomb craters), so it has real depth, not just relief.
- Distance fog fades distant geometry into the dusk sky for depth.

## Performance

The renderer is built for throughput at large draw distances:

- All wireframe segments for a frame are collected into one vertex/colour
  array and drawn with a single `glDrawArrays(GL_LINES)` per mesh, instead
  of per-object immediate-mode `glBegin`/`glEnd`.
- Terrain and cactus meshes are **cached** and only rebuilt when a relevant
  setting changes or the camera leaves the cached region; most frames do no
  geometry work at all.
- Terrain uses a **cell budget** (level of detail): the grid spacing grows
  with the rendering distance, so the cell count stays bounded as the radius
  scales up.
- Each terrain vertex's height is computed once and shared by its edges (no
  redundant noise evaluations).
- Cacti render out to the full rendering distance (like the terrain), but
  drop ring facets and trunk segments with distance so far-off ones stay
  cheap. The distance fade is done by **hardware fog** rather than the CPU.
