makepad/skills/makepad-3d-generator/references/procedural-recipes.md
Arena Agent fe21c07d84 skills: add Makepad game skills pack ported from threejs-game-skills
Nine agent skills for building Makepad/Rust games, ported from
majidmanzarpour/threejs-game-skills. Same director-routed workflow and
premium bar; runtime rewritten for this fork's game crates.

- makepad-game-director        entrypoint, routing, continuity, asset probe
- makepad-gameplay-systems     loop, movers vs rigid bodies, input, camera, netplay
- makepad-aaa-graphics-builder lighting, shaders, budget, visual scorecard
- makepad-game-ui-designer     HUD, menus, touch and XR UI
- makepad-debug-profiler       defect bisection and profiling
- makepad-qa-release           verification ladder, evidence, packaging
- makepad-3d-generator         CC0 model search, casts, procedural geometry
- makepad-image-generator      texgen textures, palettes, sky, icons
- makepad-audio-generator      sample bank, mixer, material impacts, 3D audio

Written against the real APIs in libs/game/*, libs/sim and apps/arcade:
the game.* verb table, GameRenderer adaptive quality, the packed 6-float
GameMeshVertex layout, script_mod! splash styling, makepad-test driving,
and the BUDGETS.md numbers. No paid generation API is required - the CC0
library plus seeded makepad-game-gen replaces them.

Includes install.sh (Codex/Claude), validate-skills.sh and a repo-aware
probe_assets.sh; both scripts verified against this checkout.
2026-09-05 21:18:58 +00:00

5.1 KiB
Raw Permalink Blame History

Procedural recipes

makepad-game-gen: mesh, terrain, tree, lsystem, spline, scatter, interior, kit, levelgen, implicit, texgen, cache, rng.

Determinism first

Every generator takes a seed and must produce byte-identical output on every device. Use makepad-game-gen::rng and makepad-game-math transcendentals — never std float math, never thread RNG. This is what allows content to replicate as (preset, seed, position) instead of vertex data, and it is not optional in a networked game.

#[test]
fn deterministic() {
    assert_eq!(generate_forest(99), generate_forest(99));
}

Terrain

terrain + landform::LandKind. Layer octaves of noise: a large-amplitude low-frequency pass for landform, a medium pass for hills, a small pass for surface break-up. Erode by lowering steep slopes toward their downhill neighbour over a few passes — this is what makes terrain read as landscape rather than as noise.

Resolution is a visual budget, not a sim one: 65×65 with 100 movers and 50 bodies is 0.038 ms/tick. Terrain vertices upload once per revision, so the cost is memory and vertex count, not per-frame bandwidth.

Flatten where gameplay needs flat: spawn areas, roads, building footprints. Do this after generation, as an explicit stamp, or you will fight the noise forever.

Roads and rivers — spline

Define a centreline, sample it, extrude a cross-section along it. Stamp the terrain flat under the road before placing it, or the road will float and clip in alternating segments. Bank on curves for driving. Place checkpoints (Checkpoint) at spline parameters so the track and the race logic cannot disagree.

Foliage — tree, lsystem, scatter

lsystem grows branching structures from a grammar; tree wraps the common cases. scatter distributes instances.

Scattering rules that separate a forest from a field of stamps:

  • Poisson-ish spacing, not uniform random — uniform random clumps and leaves bald patches
  • Seeded jitter: ±10% scale, full yaw
  • Exclude by slope, by height band, and by a mask around gameplay areas
  • Two or three species mixed, with one dominant
  • Density falling off with distance from the player's likely path

Replicate the field as (preset, seed, region), never as placements.

Interiors — interior

Rooms, doors, connectivity. Validate before shipping: every room reachable, doors wide enough for the player capsule, no geometry inside the spawn.

This is exactly the case the codebase learned the hard way — makepad-game-blocks dev-depends on gen because "a walker can enter a REAL generated room" is a claim about generated geometry meeting real physics, and testing the halves separately passes while the walker floats.

Levels — levelgen, kit, rtsmap

Tile-based assembly from a kit, so scale and style are consistent by construction. kit_from_index and level_placements (in makepad-game-script::compose) turn a kit into PlacedTiles. libs/rtsmap is the shared tiled-RTS generator.

Always validate:

assert!(path_exists(&level, level.spawn, level.goal), "unwinnable level, seed {seed}");
assert!(level.spawn_is_clear(), "spawn blocked, seed {seed}");

Run the validator over a few hundred seeds in a test. A generator that fails 1 in 50 seeds will ship, and the failures will be found by players.

Implicit surfaces

implicit — SDF-style blobs, caves and smooth solids, polygonised to a mesh. Good for organic rock, cave systems and merged forms. Expensive to evaluate at high resolution: generate once, cache, do not regenerate per frame.

Textures — texgen

Tiling procedural detail, packed for DrawGameTexture. The cheapest fix for "everything is flat colour", and it adds no download weight. Match tiling scale to world units so texel density is consistent across surfaces — inconsistent texel density is one of those defects everyone notices and few can name.

Meshes

mesh builds geometry directly. Emit the packed layout geom.GameMeshVertex — position 3 exact f32, octahedral normal in one lane, uv 2×f16 in one lane, colour 4×unorm8 in one lane, 6 floats / 24 B total. Attributes here are f32-only; packing means bit-packing plus unpack2f16 / unpack4u8 in-shader.

Wind vertices consistently, generate correct normals (smooth within a surface, split at hard edges), and keep UVs inside 0..1 per island.

Colliders

Never collide against the render mesh. Generate a simplified collider alongside: boxes and capsules for most things, a heightfield for terrain, a convex hull at worst. model_collider_parts(id) shows the shape the library uses.

Caching

cache, keyed by (preset, seed). Regenerating on demand during play is a stutter source; an unbounded cache is a leak. Bound it, and generate during loading where you can predict the need.

Validation checklist

  • Deterministic — same seed twice, byte-identical
  • Scaled to the player capsule
  • Packed vertex layout
  • Normals correct, winding consistent
  • Simplified collider present
  • Spawn clear, goal reachable, validated across many seeds
  • Cached with a bound
  • Visually varied — jittered scale and yaw, palette tints