makepad/skills/makepad-aaa-graphics-builder/SKILL.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

126 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: makepad-aaa-graphics-builder
description: "Raise Makepad game visuals to a premium bar: meshes, materials, shader overrides in the splash DSL, lighting and sun, shadows, baked AO, skinning, particles and fireworks, sky and fog, post look, render budget and the visual scorecard. Use for premium, AAA, polished, high-fidelity, showcase, 'less basic', art direction, and performance-versus-quality work on makepad-game-render."
---
# Makepad AAA Graphics Builder
Make it look authored. The bar is not "3D objects appear" — it is that every visible surface was decided by someone.
## Where the seam is
`makepad-game-render` owns structure; the splash DSL owns look. The engine shader decides where a spark is and how long it lives; a `script_mod!` override decides what it looks like. **Keep gameplay-relevant constants out of the DSL and look constants out of Rust.** This is what lets a generated game restyle the sky without being able to break the simulation.
```rust
script_mod! {
mod.widgets.MyView = set_type_default() do mod.widgets.MyViewBase {
draw_cube += { light_dir: vec3(0.35, 0.8, 0.45) }
draw_terrain += { light_dir: vec3(0.35, 0.8, 0.45) }
draw_skinned += { light_dir: vec3(0.35, 0.8, 0.45) }
draw_firework += { /* pixel/shape override for the look only */ }
}
}
```
Set `light_dir` consistently across **every** draw type. A skinned character lit from a different direction than the terrain it stands on is the single most common "this looks wrong and I can't say why" defect in this engine.
## The renderer
Modules in `libs/game/render`: `renderer`, `scene`, `stage`, `shaders`, `geometry`, `model`, `skin`, `shadow`, `shadow_mesh`, `particles`, `firework`, `sun`, `bake`, `ao`, `hud`, `thermometer`.
Draw types: `DrawGameCube`, `DrawGameAlpha`, `DrawGameTerrain`, `DrawGameSkinned`, `DrawGameSky`, `DrawGameShadow`, `DrawGameFirework`, `DrawGameTexture`, collected in `GameDraws`.
Frame shape:
```rust
let scene = scene_state(&world, &rig, /* .. */);
set_pass_camera(cx, &self.pass, &scene);
renderer.set_models(models); // Vec<ModelInstance>
renderer.set_particles(particles);
renderer.set_fireworks(fireworks);
renderer.draw_scene_full(cx, &mut draws, &world, &scene /* .. */);
```
Adaptive quality is already built in — do not write a second one:
```rust
let changed = renderer.report_frame_ms(ms);
renderer.set_refresh_hz(72.0);
renderer.set_shadow_budget(24);
renderer.quality(); renderer.quality_level(); renderer.quality_reason();
renderer.frame_p90_ms();
renderer.set_stage(Stage::/* .. */); // StageMode
```
Models: `load_model(..)`, `model_is_loaded(id)`, `model_bounds(id)`, `model_triangles(id)`, `model_collider_parts(id)`. `ModelInstance::new(key, vertices, indices, transform)` with `.with_tint(..)` and `.with_texture(..)`.
## Art direction first
Decide these four before authoring anything, and write them into `artifacts/game-progress.md`. Changing them later invalidates every asset choice made under the old ones.
1. **Palette** — 3 dominant + 2 accent colours. `makepad-game-assets` gives `Palette`, `Spread`, `VarietyParams`; the script route has `find_palette`.
2. **Key light** — direction, colour, and time of day. Low sun (1525° elevation) produces long shadows and reads as authored; noon light is the default-looking default.
3. **Sky and fog**`SkyConfig`, `SunConfig`, `DrawGameSky`. Fog colour must agree with the sky at the horizon or the world looks pasted onto a backdrop.
4. **Scale** — player height in world units, decided in gameplay, obeyed here. Kit pieces at inconsistent scale are instantly legible as wrong.
## Lighting
`sun.rs` and `SunConfig`. The engine's cube/terrain/skinned shaders take `sun_color`, `sun_sky`, `sun_ground` and `fog_color` as **uniforms**, not instance data — they are identical across a batch and were moved off the instance stream for a 27% saving. Do not push them back per-instance.
Three-part model: directional key (the sun), hemispheric ambient (`sun_sky` above / `sun_ground` bounce below), and fog for depth. Colour them separately — a warm key with a cool sky ambient is most of the difference between "rendered" and "lit". Grey ambient flattens everything.
Shadows: `shadow`, `shadow_mesh`, `set_shadow_budget(n)`. Shadow meshes use the same packed 6-float vertex. Budget casters — shadow every object and the frame dies; shadow only the hero and it floats. Prioritise: player, anything moving near the player, tall silhouettes.
Baked AO: `bake.rs`, `ao.rs`, `BakeSettings`, `bake_settings()` / `set_bake_settings()`, `dynamic_shade(p)`. AO in contact regions is the cheapest single upgrade to perceived quality — it is what stops props reading as stickers on the ground. The `ao_render` dev example software-rasterises props so baked AO can be judged by eye.
## Materials and variety
The library is CC0 and shared, so out-of-the-box scenes look like everyone else's. Differentiate with:
- **Tint** — `ModelInstance::with_tint`, driven by `Palette` / `VarietyParams` / `Spread`. A crowd of one prop in five palette-consistent tints reads as authored; a crowd of identical props reads as a placeholder.
- **Different members of one cast** — never clone the same character across a crowd (`find_cast`).
- **Procedural texture** — `makepad-game-gen::texgen` for tiling detail, with `DrawGameTexture`.
- **Scale and rotation jitter** — seeded, small (±10% scale, full yaw). Free, and it destroys the grid-stamped look.
## Particles and VFX
`particles.rs` (`ParticleSystem`, `ParticleInstance`), sim-side `ParticleKind` / `ParticleRequest` / `ParticleSpec` / `EmitterAnchor`, and `firework.rs` (`FireworkSystem`, `FireworkInstance`). Script verbs: `particles`, `burst`, `particles_stop`, `dust`, `beam`.
Effects read when they are **short, bright, and layered**: a flash (12 frames), a burst (0.2 s), a lingering trace (0.51 s). One long uniform puff reads as fog. Drive emission from real events — `take_mover_impacts()` — not from a polling timer.
The firework shader override in `arcade_view.rs` is the house precedent for a look override: real fireworks are *symmetric*, so pick one uniform radial angle per spark, then add swirl and fizzle so sparks look like burning matter rather than points on a sphere.
## Budgets
`apps/arcade/BUDGETS.md` is authoritative. What actually costs:
- **Not the sim.** 100 movers + 50 bodies + 65×65 terrain = 0.038 ms/tick against a 16.6 ms budget.
- **Draw calls and CPU skinning are.** Skinning is CPU-side, so the whole skinned buffer re-uploads every frame. The Knight at 3716 verts was 238 KB/frame at 64 B/vertex, and 89 KB at the packed 24 B.
- Vertex attributes here are **f32-only** — no u8/u16/i16 attribute types. Compression means bit-packing into f32 lanes and unpacking in-shader with the `unpack2f16` / `unpack4u8` builtins (available on Metal/GLSL/HLSL/WGSL).
- `geom.GameMeshVertex` is the shared packed layout: position 3 exact floats, octahedral normal in 1 lane, uv as 2×f16 in 1 lane, colour as 4×unorm8 in 1 lane = **6 floats / 24 B**. Use it.
- Quest is **bandwidth-bound before ALU-bound**. Cut bytes per frame first, shader instructions second.
- Measure from `RenderStats::instance_floats`, not by counting fields by hand.
## Order of visual return
When time is limited, this order gives the most visible improvement per hour:
1. Key light direction + colour (low sun)
2. Contact shadows / baked AO
3. Palette discipline and tint variety
4. Sky/fog agreement at the horizon
5. Silhouette clarity of the hero
6. Ground detail — texture or geometry, not flat colour
7. Impact VFX on every hit
8. Post look and grading
## Verification
Screenshot every state at target aspect ratio and **look at them**. Score with `references/visual-scorecard.md`: premium means nothing below 2 and an average ≥ 2.3. Read `frame_p90_ms()` before and after any performance claim. `cargo run -p makepad-arcade --example bigworld_probe --release` gives headless render evidence.
## Required reading
- `references/visual-scorecard.md` — the 10 categories, anchors, and how to score.
- `references/shader-cookbook.md` — DSL overrides, packing, the builtins.
- `references/lighting-and-materials.md` — sun, ambient, fog, AO, tint variety.
- `references/render-budget.md` — what costs, how to measure, what to cut first.