Skip to content

Blender pipeline overview

New to level-making?

Start at the Making a level hub for the newcomer reading order, the which-doc-for-whom map, and the two authoring workflows.

The Hoverbike asset pipeline runs through Blender 5.1+. Most of what you build in Blender exports to a single .glb per track or per bike, which the runtime loads at boot. The pipeline has three distinct surfaces:

  1. The in-Blender addon (tools/blender/hoverbike_addon/) — a sidebar in Blender's 3D viewport with one-click Export to Game buttons, parametric authoring tools (road, tunnel, ramp, downtown, horizon, wave zones, particle emitters), and live previews of gates / racers / water / boost pads / wave zones that follow your edits.
  2. Headless builders (tools/blender/build_*.py) — Python scripts run via blender --background. Pair with JSON specs to produce GLBs in CI / batch. The sprite atlas behind every particle emitter is built by the same family (build_sprite_atlas.py).
  3. Seed scripts (tools/blender/seed_*.py) — one-shot generators that materialise canonical .blend files (template islands / alpine / dunes / mesa / downtown / tunnels, prop and landmark libraries, calibration scene) from code.

Authoring usually means opening an existing .blend, editing, clicking Export. The seed scripts are how those .blends get created in the first place; the headless builders are how CI keeps the canonical scenes in sync with their JSON specs.

What the addon gives you

Once installed, press N in any 3D viewport → Hoverbike tab to reveal the panel. It re-renders based on whether the open .blend lives in tracks-src/ or bikes-src/. There's also a top-bar Hoverbike menu (Add / Build / Spline / Terrain / Thumbnail / Utility submenus) and a Shift+H pie menu for keyboard-driven workflow — both register the same operators as the sidebar.

In track mode the panel exposes ~24 selection-driven sub-panels. Most are scoped: they only render when an object of the matching kind is active in the viewport (e.g. the Wave zones sub-panel only appears when a wave_zone_NN empty is selected). This keeps the sidebar short during normal editing; use the Hoverbike → Add menu to spawn an object of a given kind and the matching sub-panel appears.

Sub-panelDrops you into
Spline toolsEditing the racing line, snapping it to terrain, auto-placing ramps at curvature peaks.
Road toolBezier curve → drivable road slab with banking, F1 curbs, terrain conform.
TunnelsBezier curve → boolean cut through the hill + concrete-liner interior shell.
Placement helperA persistent curve-constrained empty for parking ramps / boosts / props at any (t, offset).
DowntownProcedural city block at the 3D cursor (multi-block grid, terrain-conformed plinth).
RampsDrop a parametric stunt wedge at the 3D cursor.
TerrainHeightmap import, sculpt-mode entry, raise/lower at cursor, smooth, AO + path-wear bakes.
Terrain templatesOne-click Add Island Terrain / Add Multi-Biome Terrain starter landforms + island mod-zones.
Terrain materialAdds the runtime-matching terrain material so the viewport previews the in-game shading.
WaterSea-level slider + wave preview plane.
Gate buoysAuto-places marker buoys where the racing line crosses open water (needs a water reference present).
HorizonPer-track distant silhouette mesh (or procedural ring fallback).
Sky presetPer-track tint, cloudiness, sun, fog, time-of-day, colour grade, sea-state Beaufort.
Wave zonesPer-zone wave amplitude / frequency / surge multipliers. See Wave zones cookbook.
GameplayGates, boost pads, racer preview, turn indicators — placement + previews.
PropsPlace library props (prop_NN) along the track.
Prop linesParametric "asset along a curve" — scatter a prop down a spline at a given spacing.
DecalsThin projected decal meshes (paint, signage, scorch) laid onto terrain / geometry.
Scatter / biome palette / scatter strokeFoliage subsystem — scatter zones, per-biome palette masks, and hand-painted scatter strokes.
EmittersParticle systems for VFX (steam, foam, embers, gulls, neon, ash).
Ghost lap + chase camAuto-flying preview bike along the racing line.
Terrain shader (runtime)Tunes the runtime ramp / slope / wet-band / coloration without touching the .ts.
Track hero renderCamera-driven 1280×720 hero + 320×180 tile for the loading screen.
Track statsSpline length, lap-time estimate, terrain min/max y, water coverage.

In bike mode the panel is much smaller — header with bike id, Export Bike to Game, Copy Play / Viewer URL.

Both modes share a Lint button (pre-export sanity check) and a Reload from JSON button (pulls scalar fields back from the runtime JSON into the scene custom properties).

For the full operator + panel reference, see Addon reference. For worked, end-to-end examples of the wave-mastery tools see the Wave zones cookbook.

What lives where

text
tools/blender/
├── hoverbike_addon/             ← in-Blender addon (package)
│   ├── __init__.py              ← bl_info + per-module register
│   ├── panel.py                 ← sidebar UI (parent + sub-panels; more register from modules)
│   ├── menu.py                  ← top-bar menu + Shift+H pie menu
│   ├── prefs.py                 ← addon preferences (project-root path, etc.)
│   │
│   ├── road.py · road_conform_gn.py · tunnel.py · ramp.py · downtown.py
│   ├── terrain.py · terrain_material.py · island_terrain.py · multibiome_terrain.py
│   ├── spline.py · placement_helper.py · turn_indicators.py
│   ├── water.py · wave_zone.py · gate_buoys.py · horizon.py · sky_preset.py
│   ├── antigrav.py · antigrav_ribbon.py   ← PARKED (anti-grav cut from races; kept for a possible DLC)
│   ├── emitter.py · boost_pad.py
│   ├── prop_placements.py · propline_placements.py · prop_bake.py · decal.py
│   ├── scatter.py · scatter_stroke.py · biome_palette.py   ← foliage subsystem
│   ├── previews.py · ghost_lap.py · thumbnail.py · bake.py
│   ├── export.py · track_meta.py · terrain_shader.py · auto_tag.py
│   ├── new_map.py               ← "duplicate a template" wizard
│   ├── handlers.py              ← live-preview auto-rebuild
│   └── _legacy.py               ← validation + JSON sync (shared infra)

├── build_bike.py                ← headless bike → GLB
├── build_track.py               ← spec-driven track → .blend + GLB
├── build_prop.py                ← spec-driven prop → GLB
├── build_sprite_atlas.py        ← packs particle-atlas.png (Pillow)

├── seed_template_island.py      ← procedural island scene
├── seed_template_alpine.py      ← procedural alpine scene
├── seed_template_dunes.py       ← procedural dunes scene
├── seed_template_mesa.py        ← procedural mesa scene
├── seed_template_downtown.py    ← procedural downtown scene
├── seed_template_tunnels.py     ← procedural tunnels-through-hills
├── seed_template_antigrav_showcase.py ← PARKED — anti-grav tube/ribbon/strip reference (cut from races)
├── seed_props_library.py        ← rebuilds tracks-src/props-library.blend
├── seed_landmarks_library.py    ← rebuilds tracks-src/landmarks-library.blend
├── seed_prop_kit.py             ← rebuilds tools/blender/lib/prop_kit.blend

├── seed_track_*.py              ← per-track seeders (one per ship track)

├── lint_track.py                ← CI lint entry (same checks as the addon)
├── run-lint.mjs                 ← Node wrapper for batch lint
├── run.mjs                      ← Node CLI for headless batch builds
├── install-addon.mjs            ← symlinks the addon for live editing
├── test-addon.mjs               ← smoke-tests addon registration
├── inspect_glb.mjs              ← quick GLB inspection (extensions, nodes)

├── common.py · sockets.py · colliders.py · mounts.py
├── hoverbike_kinds.py           ← ExportedKind / AuthoringKind enums
└── lib/                         ← committed source-art kits
    └── prop_kit.blend

Outside tools/blender/ the relevant directories are:

PathContents
bikes-src/<id>.blendOne per bike — the geometry source of truth.
tracks-src/<id>.blendOne per track — editing this is the track workflow. The tracks-src/ folder can live in your clone or a Drive-synced folder outside it; either way the .blend is gitignored.
tracks-src/props-library.blendLinked-library source for prop_gate_mesh (the real gate prop) and other shared props.
tracks-src/landmarks-library.blendLinked-library source for per-city skyline / landmark meshes.
tracks-src/template-*.blendSeed-generated template scenes (island, alpine, dunes, mesa, downtown, tunnels; antigrav-showcase is parked). Start a new track by duplicating one with Hoverbike → Utility → New Map from Template.
specs/bikes/<id>.jsonSlim metadata + recolour overrides for each bike.
specs/tracks/<id>.jsonDeclarative spec for spec-driven tracks (the headless builder reads these).
specs/props/<id>.jsonParametric specs for kit-assembled props.
public/assets/tracks/<id>.glbOutput track environment GLBs.
public/assets/tracks/<id>-hero.jpg / -thumb.jpgLoading-screen + tile art rendered from camera_hero.
public/tracks/<id>.jsonOutput track gameplay data — round-trips with the .blend.
public/assets/bikes/<id>.glbOutput bike GLBs.
public/assets/fx/particle-atlas.pngShared 1024×1024 4×4 sprite sheet for every emitter.
public/assets/manifest.jsonIndex of every built asset (driven by gen:tracks / gen:bikes / gen:props).

One-time setup

  1. Install Blender 5.1+. Typical paths:

    • Windows: C:\Program Files\Blender Foundation\Blender 5.1\blender.exe
    • macOS: /Applications/Blender.app/Contents/MacOS/Blender
    • Linux: /opt/blender/blender
  2. Set BLENDER_EXE if Blender isn't on your PATH — the headless builders and pnpm gen:* scripts use it to find the executable. Add to your shell rc or set it per-session.

  3. Install the addon as a symlink so every edit in the repo is live in Blender after F3 → Reload Scripts:

    bash
    pnpm install:blender-addon

    The script symlinks tools/blender/hoverbike_addon/ into your Blender user scripts directory (%APPDATA%\Blender\5.1\scripts\addons\ on Windows, ~/Library/Application Support/Blender/... on macOS, ~/.config/blender/... on Linux). On Windows without Developer Mode it falls back to a recursive copy; the script prints how to enable it. The script also backs up any leftover legacy single-file hoverbike_addon.py from before the package split to .bak so it can't collide with the package.

  4. Enable the addon in Blender. Edit → Preferences → Add-ons → search "Hoverbike" → tick the checkbox. You only need to do this once. After that, the Hoverbike menu appears in the top bar and the N-panel → Hoverbike tab appears in the 3D viewport.

  5. Smoke-test the install:

    bash
    pnpm test:blender

    Runs Blender headless, registers the addon, asserts every operator + panel registers cleanly.

If a panel or operator disappears from the N-panel after pulling, the installed addon has drifted — re-run pnpm install:blender-addon (or check that the symlink wasn't broken by a deleted worktree).

The contract: Blender objects ↔ runtime

Every metadata-bearing object in a .blend has a kind custom property declaring what the runtime should do with it. The exporter copies custom properties verbatim into glTF extras; the runtime loader (src/game/tracks/glb-loader.ts) walks the GLB nodes and reads extras.kind.

For the full naming + extras matrix see Scene conventions.

Where to go next

Made with VitePress.