Builder now uses the furniture OSM already carries (footways, lamps, benches, bins, bollards, bus stops, crossings): raised chamfered footpaths, shopfront bands, awnings in three fabrics, parapets, layered fig/palm trees, zebra bars, dashed centre lines, Queen St Mall winged canopies. ACES tonemap + SSAO + fog in game.gd. flux_texture.sh gains size/luma clamping (FLUX renders "pale" as blown-out white); gen_textures.sh + check_textures.py manage the set. Woolies carpark rebuilt textured, parking the real fleet. Fixes: fractional OSM layer tag crash, zero-length-normal UV projection, blood-red mall pavers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.2 KiB
Burnout Infinity: Shitbox
Burnout 3-style arcade racer, Aussie shitboxes, Woolies carparks. Godot 4.6 + Blender, native Apple Silicon.
Run
/Applications/Godot.app/Contents/MacOS/Godot --path .
Controls: WASD/arrows drive, Shift drift, Space boost, R reset (or retry after Crash Mode), Esc menu.
Modes
- Cruise — free roam with traffic.
- Crash Mode — 60 seconds, rack up damage dollars ramming traffic and parked cars.
- Crash Junctions — pick a "CRASH JCT" entry in the level list: launch into scripted
cross-traffic convoys at a real intersection, then Aftertouch-steer your wreck (arrows)
and detonate the Crashbreaker (space). Damage tally + bronze/silver/gold. Events live in
levels/<id>/junctions.json; generate more from real intersections withtools/osm_junction.py <area.json> <out.json> <lat0> <lon0> "Name|Street A|Street B". - Race — 3 laps vs 3 rubber-banded rivals on the level's
RacePath.
The boost loop is Burnout 3's: earn boost by drifting, near-missing traffic, and smashing things. Wrecking a rival near you = TAKEDOWN — refills the meter and grows it (up to 4x). Hard crashes shed body panels and trigger Impact Time slow-mo.
Cars
A car is a folder: cars/<id>/stats.tres + optional car.glb. Models are built
procedurally — specs live in tools/build_cars.py, one dict per car
(silhouette profile, dimensions, paint). Rebuild all GLBs with:
/Applications/Blender.app/Contents/MacOS/Blender -b -P tools/build_cars.py
Model convention (any GLB works, hand-modelled or generated):
- Nose faces +Y in Blender (imports as Godot -Z forward).
- Node origin 0.75 m above ground level (the controller's ride height).
- Objects named
det_*are detachable — the game rips them off on impact (doors, mirrors, bumpers, bonnet, boot). - Current fleet: Datto 120Y, Gemmy TX, Kingswood HQ, VL Terbo, XF Fungoon, Excel Hilton. Piss-take names on purpose: real model names and badges are trademarks.
To improve a silhouette: supply a side-on photo + front three-quarter photo, tweak the profile points in the spec, rebuild, screenshot.
Stats are low-key reversed: the shitboxier the car was in real life, the better it performs here. The Excel X3 is the fastest car in the game; the muscle cars are all bark.
Downloaded models (from shitboxes/, gitignored + .gdignore'd) convert via:
/Applications/Blender.app/Contents/MacOS/Blender -b -P tools/import_shitbox.py -- "<file>" <car_id> <length_m> [yaw_deg]
Imported cars don't shed det_* panels until someone cuts their doors out in Blender.
Real-world levels from OpenStreetMap
Real street grids, our art style, clean collision, ODbL-licensed. Fetch a bbox from the OSM API, convert, build (queen_st_mall was made exactly like this):
curl -A 'BurnoutShitbox/1.0' 'https://api.openstreetmap.org/api/0.6/map?bbox=<minlon>,<minlat>,<maxlon>,<maxlat>' -o area.xml
python3 tools/osm_xml_to_json.py area.xml area.json # see git history / osm_level_args.py
ARGS=$(python3 tools/osm_level_args.py area.json "<spawn way name>" "<loop street 1,2,3,4>")
Blender -b -P tools/build_level_osm.py -- area.json <level_id> <origin_lat> <origin_lon> $ARGS
Don't reconstruct from Street View imagery (ToS + melted collision). Use photos as reference to hand-detail hero landmarks on the OSM base.
Levels so far: woolies_carpark, queen_st_mall, southbank, albert_parklands, story_bridge (procedural steel truss along Bradfield Highway), kangaroo_point.
Area JSONs are cached in ~/.cache/burnout_osm (override with $OSM_CACHE);
tools/build_all_levels.sh rebuilds every level from them.
What the builder makes of the OSM data
Beyond the street grid and building footprints, it uses the furniture OSM already carries — street lamps, benches, bins, bollards, bus stops and the whole footway network — so most of the detail is real Brisbane, not invented:
- Buildings extrude with a glazed shopfront band at street level, a cantilevered awning over the footpath (three canvas colours, varied heights, ~70% of buildings — a whole street at one height in one colour reads as a painted stripe), a roof parapet, and a plant room on anything tall.
- Footpaths come from the OSM footway ways, raised 14 cm with a chamfered kerb. The chamfer is deliberate: a hard vertical edge at 120 km/h reads as a crash to the jolt detector in car.gd.
- Roads get dashed centre lines;
footway=crossingbecomes zebra bars laid flat on the road instead of a raised strip you'd launch off. - Trees are a trunk plus three offset welded spheres (figs) or radiating fronds (palms), scattered along kerbs on top of the ones OSM maps — Brisbane has far more street trees than are tagged.
- Queen Street Mall's winged canopies grow along pedestrian ways whose name
starts with the
canopyargument. Note OSM splits the mall into a 15 m stub named "Queen Street Mall" plus the 438 m pedestrian way named just "Queen Street", hence prefix matching.
Variation is seeded off OSM ids, so rebuilds are reproducible.
Textures
assets/textures/ is FLUX-generated (Cloudflare Workers AI). Regenerate the
whole set with tools/gen_textures.sh or one with
tools/gen_textures.sh <name>; creds are read from .env, never printed.
Two things worth knowing before you re-prompt:
- FLUX returns a near-white blank sheet for anything described as pale ("off white metal panel" came back at 240/255 however it was worded), and a blown-out albedo renders as a featureless void in game. The spec table's last column is a target mean luma that clamps this deterministically.
tools/check_textures.pyreports mean/contrast for the set and flags the duds. Run it after regenerating.
The builder box-projects UVs at a per-material metres-per-tile scale; facades pick glass/brick/concrete/heritage/sandstone by height, building tag and id hash.
woolies_carpark is the one level still built in GDScript rather than as a GLB
(carpark.gd) — its parked shitboxes have to
stay shuntable RigidBodies, which a static GLB can't carry. It picks its parked
cars at random from the real fleet. Its Trolley Run junction runs convoys along
the aisles at z = 15/12 and z = -21/-24, so keep those lanes clear.
Add a level
Make a folder levels/<id>/ containing level.glb (or level.tscn). Blender convention:
- Solid objects: suffix the name with
-col(Godot generates collision on import). - An Empty named
Spawnwhere the car starts. - A curve/path named
RacePath(loop) enables Race mode and traffic. - Export glTF Binary to
levels/<id>/level.glb. It appears in the menu.
Tests
/Applications/Godot.app/Contents/MacOS/Godot --headless --path . --import
/Applications/Godot.app/Contents/MacOS/Godot --headless --path . --script tests/smoke.gd
Visual check (opens a window, saves a PNG):
/Applications/Godot.app/Contents/MacOS/Godot --path . --script tests/screenshot.gd -- car=datto_120y mode=crash frames=280 out=shot.png
Design doc
docs/BurnoutInfinityShitbox.pdf — research report on the original game. Still to come:
crash deformation (control-hull vertex displacement, equations in the PDF), oncoming-lane
boost, Road Rage mode, sound, real level GLBs from Blender.