# Texture workflows ## Pipeline ``` texgen (seeded) -> packed texture -> DrawGameTexture -> ModelInstance::with_texture(idx) ``` Generate at load, cache by `(preset, seed)` in `makepad-game-gen::cache`, and bound the cache. Generating textures during play is a stutter source; an unbounded cache is a leak. ## Layer order Always build in this order — later layers depend on earlier ones being settled: 1. Base value 2. Large low-frequency variation 3. Structure (planks, bricks, grain) 4. Small detail (grit, scratches) 5. Edge wear If a texture is not working, the fault is almost always layer 1 or 2. Adding more detail to a wrong base value never fixes it, and detail is the layer that costs the most to compute. ## Seamless tiling Wrap the noise domain so opposite edges match. Test the tile by rendering it 4×4 and looking for: - A visible grid → base contrast too high, or a distinctive feature repeating - A diagonal → directional noise not wrapped in both axes - Bright/dark blocks → low-frequency layer wavelength larger than the tile Fixes, cheapest first: lower the base contrast; move the distinctive feature to a per-instance decal instead of into the tile; add per-instance yaw and tint jitter; only then increase tile size. ## Texel density Pick one target — say 256 texels per world metre — and derive every tiling scale from it. A crisp crate beside a blurry wall is a defect everyone sees and few can name. Write the target into `artifacts/game-progress.md` beside the palette. ## Value structure Convert a gameplay screenshot to greyscale. Foreground, midground and background must read as three distinct bands. If they merge, adjust *value*, not hue — hue changes alone will not fix a flat read, and this is the most common mistake when a scene "looks muddy". ## Palettes in code `Palette`, `Spread`, `VarietyParams` from `makepad-game-assets`; `find_palette` on the script route. ```rust let tint = palette.sample(&variety, seed_for(entity_id)); ModelInstance::new(key, verts, idx, xform).with_tint(tint) ``` Seed the sample per entity so it is stable across frames and identical on every client. A tint that changes each frame flickers; a tint derived from an unseeded RNG desyncs in netplay. ## Resolution guidance | Surface | Size | | --- | --- | | Large ground / terrain, tiled | 512² tiled, not one huge map | | Building walls | 512² | | Props | 256² | | Distant scenery | 128², or vertex colour only | | HUD icons | drawn geometry, not a bitmap | Textures are bandwidth, and Quest is bandwidth-bound before ALU-bound. Match resolution to on-screen size; a 2048² map on a distant prop is pure cost. ## Sky and fog ```rust SkyConfig { /* horizon colour, zenith colour, sun disc */ } ``` Checks that catch most sky problems: - Fog colour equals the sky colour **at the horizon**, not at the zenith - Sun disc position agrees with `light_dir` on every draw type - Horizon gradient band is wide enough not to show a hard line - Sky brightness matches the time of day the shadows imply ## Icons Draw as geometry or SDF in the widget layer. Checklist: readable at 24 px; meaning never carried by hue alone; consistent stroke weight and corner radius across the set; control glyphs matching the active input device. ## Verification - View in-engine at the real distance and angle — not in a texture viewer - 4×4 tile test for seams - Greyscale test on a gameplay screenshot - Texel density compared against an adjacent surface - Memory: total texture bytes against the device budget - Score scorecard categories 3 (materials), 4 (ground), 6 (colour), 9 (sky)