◐
material · drop-in viewer
asset⌬ materialmaterialprimary: mat.yaml·part ofpackage weatheringMaterials.package·originates fromworld 5e63a079-a…
What it does
No description provided yet.
Interface
What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.
This asset doesn't declare a schema or structured payload.
⌬ Metadata
description
Sub-parts
Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.
Showing up to 3 levels deep. Open a part to see what it holds further down.
10+items
▲ 0↑ born
# weatheringMaterials: Wear and Tear
**28 weathering overlays for the Swatchbook material library (materialLibraryKit): the wear and life that sits on top of a surface, so any wall, floor, car or crate goes from brand new to lived-in.** Dirt and grime, water, corrosion and growth, damage, cover, marks and signs, and four cartoon ones for toon worlds. An overlay is laid over whatever material a surface already wears, from any collection, with an amount from none to full that you can change while the world runs; a sticker is placed on a surface like a decal you can size in metres, turn and lay several of side by side. Both are lit by the scene, and every player sees the same overlays, stickers and amounts.
Version 1.0.11. Depends on materialLibraryKit 1.3.1 (Swatchbook) and nothing else; installing this package brings the library along (about 914 files, a few seconds), and its shaders use the engine's own built-in shader pieces, so nothing of the engine is copied in. Once installed, every overlay is in the library's list, picker and swatch gallery on every player's machine, with no setup code.
## Install and weather your first wall
Install **weatheringMaterials** from ZeroMind (search "weatheringMaterials" or "Wear and Tear"). Then, in an empty world, from the console or any script:
```luau
local Weathering = require("~weatheringMaterials.Weathering")
-- a brushed-metal wall (the engine's own metal material), synced so every player sees what weathers it
local wall = entity.spawn("rustyWall", { synced = true, components = { Model = { model = "cube", material = "@builtin::materials.metal_brushed" } } })
wall.localPosition = { 0, 1.5, 4 }
wall.localScale = { 4, 3, 0.2 }
assert(Weathering.add(wall, "rustStreaks", 0.2)) -- rust running down from its top edge and bolts
assert(Weathering.set(wall, "rustStreaks", 0.8)) -- change the amount live, for every player
local tag = assert(Weathering.stick("graffiti", { design = "tag", position = { 0, 1.4, 3.9 }, normal = { 0, 0, -1 }, size = 1.6 }))
```
The wall wears the engine's brushed metal (`@builtin::materials.metal_brushed`, in every world), so the rust lands on metal straight away; any other material, from any collection, takes it the same way. `Weathering.add` and `Weathering.set` return true or false and the reason, and `Weathering.stick` returns the sticker or nil and the reason; the `assert` stops with that reason instead of leaving the wall bare. The wall's front faces -Z here, so the tag is placed on that face with `normal = { 0, 0, -1 }`. To take it all away again: `Weathering.unstick(tag)` for the sticker (`Weathering.stickers("graffiti")` lists the graffiti on this machine), `Weathering.remove(wall)` for the rust, and `entity.despawn(wall.id)`. Fade the rust in instead: `Weathering.fade(wall, "rustStreaks", 1, 5)` takes it to full over 5 seconds on this machine, and every other player sees it at full when the fade ends. The shaders compile the first time a world draws them, so the rust can take a minute to appear in a new world (see How the package works).
## Texture memory
**1.7 MB as the library measures it, 4.3 MB with every picture design placed** (GPU memory, mipmaps included; UASTC, which the engine turns into BC7 on desktop and ASTC on phones). Only the picture stickers carry textures, every one a power of two on each side: the graffiti tag and piece (683 KB each, 1024 x 512), the torn poster (683 KB, 512 x 1024), the flyer layer (1.3 MB, 1024 x 1024), and the boot, bare and paw prints (341 KB each, 512 x 512). The library measures each entry by its own material, which shows the entry's first design: graffiti 683 KB, posters and flyers 683 KB, footprints 341 KB, 1.7 MB in all, the figure `Checks.budget` and `Checks.textureMemory` report and the gallery labels show. Every overlay, and the procedural stickers (splat, drips, puddle, oil stain, soot plume, spider crack, gouges, skid marks, mud tracks), is drawn by its shader and costs no texture memory. The 28 reference pictures the gallery hangs add 37.3 MB; a world that does not open the gallery never loads them.
Every material passes the library's web check (`Checks.budget`): at most 1 sampled texture of its own (the web limit is 9) and no storage buffer. An overlay is drawn as its own pass over the surface, so it adds nothing to the base material's bindings; read together as the stricter measure, the heaviest pairing, a full engine PBR base (5 texture slots) under any overlay or sticker, binds 6 textures, inside the 9. `Weathering.budget(baseMaterial, id)` answers it for any pair.
## The overlays
Every look also says how it goes on and which masks it uses. `application` is `"layer"` (laid over whatever a surface already wears, by an amount), `"decal"` (stuck on a surface as a sticker, placed, sized and turned) or `"both"`; `maskKinds` lists where its layer gathers (for example `{ "up-facing", "crevices" }`, with `"placed"` for a decal). Read them through `Weathering.get(id)` and `Weathering.list(filter?)`, which hand out each look as the library's own entry plus `application`, `maskKinds` and `family`, all declared in the exported `Weathering.Look` type, so a strict script reads them like any other field: `local look = Weathering.get("rustStreaks"); print(look.application, table.concat(look.maskKinds, ", "))` prints `layer streaks, top edges, bolt rows`, and `Weathering.list({ application = "decal" })` lists the 11 that go on as decals (`"both"` counts as either). The same values are written into the collection file as `application:` and `maskKinds:` and set on the library's entries on every machine; the library core's own entry type is fixed and does not declare them, so from `Materials.get` a strict script would be refused, and `Weathering.get` is the way to read them. The table below lists both.
Every entry is tagged `weathering`, its family (`dirt`, `water`, `corrosion`, `growth`, `damage`, `cover`, `marks`, `toon`), its kind, how it goes on (`layer` or `decal`, both for a look that goes on either way), its form (`overlay`, `sticker`, or both, tagged `sticker and overlay`, `sticker` and `overlay`), how it is masked (`mask-up-facing`, `mask-height`, `mask-crevices`, `mask-edges`, `mask-streaks`, `mask-noise`, `mask-everywhere`, `mask-low spots`, `mask-rising plumes`, `mask-top edges`, `mask-bolt rows`, `mask-placed`), its tile size (`tile-<metres>m`), `animated` or `not-animated`, how many textures its designs carry (`0-textures`; `2-textures` for graffiti and posters, `3-textures` for footprints, one per design) and their cost (`textures-none`, `textures-medium`), `realistic` or `toon`, `no baked light`, each sticker design (`design-tag`, `design-piece`...) and search words. Find them with the library: `Materials.list({ collection = "weathering", tag = "sticker" })`, or with `Weathering.list({ family = "water", application = "layer" })`.
| Id | Name | Family, kind | Goes on as (`application`) | Gathers | `maskKinds` | Tile | Moves | Textures |
|---|---|---|---|---|---|---|---|---|
| `dirtFilm` | Dirt film | dirt, dirt film | layer | low on the object, washes down from the top edge, faint blotches | `height`, `streaks`, `noise` | 1.2 m | no | 0 |
| `settledDust` | Settled dust | dirt, dust | layer | thick on what faces up, a thin film on the sides, crevices | `up-facing`, `everywhere`, `crevices` | 1 m | no | 0 |
| `creviceGrime` | Crevice grime | dirt, grime | layer | crevices (read from the scene), a contact band at the foot | `crevices`, `height` | 0.6 m | no | 0 |
| `sootStains` | Soot and smoke stains | dirt, soot | both | plumes rising up walls, under top edges; sticker: a plume over a vent | `rising plumes`, `top edges`, `placed` | 1 m | no | 0 |
| `oilStains` | Oil stains | dirt, oil | both | facing up, blotches; sticker: one stain | `up-facing`, `noise`, `placed` | 0.7 m | no | 0 |
| `wetSurface` | Wet surface | water, wet | layer | everywhere, drying in patches | `everywhere`, `noise` | 1.6 m | no | 0 |
| `puddles` | Puddles | water, puddle | both | flat up-facing low spots; sticker: one puddle | `up-facing`, `low spots`, `placed` | 2.4 m | rain rings | 0 |
| `waterStains` | Water stains and tide marks | water, tide marks | layer | by height, runs | `height`, `streaks` | 0.8 m | no | 0 |
| `risingDamp` | Rising damp | water, damp | layer | by height from the foot, ragged top, salt line | `height` | 1.2 m | no | 0 |
| `rustStreaks` | Rust streaks | corrosion, rust streaks | layer | down vertical faces from top edges and bolts | `streaks`, `top edges`, `bolt rows` | 0.3 m | no | 0 |
| `rustBloom` | Rust bloom | corrosion, rust patches | layer | spots everywhere, crowding at edges | `noise`, `edges` | 0.5 m | no | 0 |
| `mossCreep` | Creeping moss | growth, moss | layer | facing up, edges, crevices | `up-facing`, `edges`, `crevices` | 0.6 m | no | 0 |
| `lichenSpots` | Lichen spots | growth, lichen | layer | scattered, more on top | `noise`, `up-facing` | 0.12 m | no | 0 |
| `cracks` | Cracks | damage, cracks | both | a hairline network; sticker: a spider crack | `noise`, `placed` | 0.7 m | no | 0 |
| `chippedPaint` | Chipped paint | damage, paint chips | layer | edges and corners | `edges` | 0.14 m | no | 0 |
| `scratches` | Scratches and gouges | damage, scratches | both | everywhere, noise; sticker: deep gouges | `noise`, `everywhere`, `placed` | 1 m | no | 0 |
| `snowCover` | Snow cover | cover, snow | layer | facing up only | `up-facing` | 0.8 m | no | 0 |
| `lightFrost` | Light frost | cover, frost | layer | everywhere, heavier on tops and edges | `everywhere`, `up-facing`, `edges` | 0.6 m | no | 0 |
| `sandDrift` | Sand or ash drifts | cover, sand | layer | crevices, the foot, ledges (variant 1: grey ash) | `crevices`, `height`, `up-facing` | 0.8 m | no | 0 |
| `graffiti` | Graffiti | marks, graffiti | decal | placed: `tag` 1.5 x 1.0 m, `piece` 4.2 x 2.8 m | `placed` | 2 m | no | 1 each |
| `paperPosters` | Posters and flyers | marks, posters | decal | placed: `poster` 0.8 x 1.2 m, `flyers` 1.0 x 1.0 m | `placed` | 1 m | no | 1 each |
| `paintSplats` | Paint splats and drips | marks, paint | decal | placed: `splat` 1 x 1 m, `drips` 0.9 x 1.6 m (run down) | `placed` | 1 m | no | 0 |
| `footprints` | Footprints | marks, prints | decal | placed: `boot`, `bare`, `paw` trails | `placed` | 1.5 m | no | 1 each |
| `tyreMarks` | Tyre tracks and skid marks | marks, tyre marks | decal | placed: `mud` 3.0 x 1.8 m, `skid` 4.0 x 1.8 m | `placed` | 3 m | no | 0 |
| `toonDirtPatches` | Toon dirt patches | toon, dirt | layer | low on the object, noise | `height`, `noise` | 1 m | no | 0 |
| `toonSnowCap` | Toon snow cap | toon, snow | layer | facing up | `up-facing` | 1 m | no | 0 |
| `toonRust` | Toon rust | toon, rust | layer | noise, edges, runs | `noise`, `edges`, `streaks` | 0.8 m | no | 0 |
| `toonSplat` | Toon splat | toon, paint | decal | placed: `splat` | `placed` | 1 m | no | 0 |
Tile is the metres one repeat of an overlay's main pattern covers (1 unit = 1 m); a sticker's is its typical size. `WeatherEntries` holds every entry's exact tags, colours and settings.
## How it looks right
- **Nothing is painted with light.** Every overlay hands the engine a surface (colour, roughness, metal, relief) and the engine lights it with the scene's sun, sky, lamps and shadows, the same as the material under it. The reference pictures are photographs and the gallery shows them unlit beside each bay; the overlays themselves carry no light or shadow.
- **Water darkens and shines.** A wet overlay does not paint a dark colour: it darkens what it covers by the share water fills its pores (about half for concrete and brick) and lays a clear, glossy water film over it with its own reflections, so a wet surface is darker and shinier at once, and dries back in patches as the amount falls. Puddles only stand on flat up-facing ground, in its low spots, with a dark wet rim and rain rings.
- **Snow sits on what faces up.** Snow, dust, frost on tops and sand on ledges follow the surface's real facing in the world, so a wall's side stays bare however the wall is turned.
- **Rust and drips run down.** Streaks start at an object's top edge and at rows of bolts and run straight down its vertical faces, fading as they go; paint drips and soot plumes keep to gravity on a wall whatever their turn.
- **Chips follow the edges.** Chipped paint, edge rust and moss read the object's own edges (its box edges, and tight curvature on rounded shapes) and bare the metal under the paint with a primer rim.
- **Grime finds the crevices.** Crevice grime, moss and sand look at the scene around each point (a small occlusion probe over the depth already drawn), so the corner where a wall meets the pavement collects grime although the two are separate objects.
## Using an overlay
`Weathering.add(target, id, amount?, opts?)` lays overlay `id` over the target's Model and every Model under it, whatever material each wears, for every player. Under the hood each Model gets a shell: a temporary entity each machine makes for itself, with the same mesh and the overlay's material, drawn a couple of millimetres off the surface and blended over it. A shell is not a child of the object it covers: it stands on its own in the scene and takes the object's position, turn and size every frame, so it moves with the object. Overlays stack: add as many as you like to one object, one shell per overlay per Model.
| Call | Does |
|---|---|
| `Weathering.add(target, id, amount?, opts?)` | Lays it, for every player. `amount` 0..1 (default 1). `opts.mask` switches this instance to one mask: `up`, `height`, `crevices`, `edges`, `noise`, `streaks`, `everywhere` or `painted`; `opts.seed` (0..1) moves the pattern; `opts.variant = 1` takes the variant colours (`sandDrift`: grey ash); `opts.brush = { x, y, z, radius }` with `mask = "painted"` paints it in a sphere (metres from the object's middle); `opts.localOnly` keeps it on this machine. |
| `Weathering.set(target, id, amount, opts?)` | Changes the amount live, for every player (lays it if it is not there). `opts.localOnly` changes it on this machine only, and never over an amount the room shared. |
| `Weathering.fade(target, id, amount, seconds)` | Eases to an amount over `seconds`: this machine eases it, every other player sees the final amount when the fade ends, and that amount is shared. |
| `Weathering.remove(target, id?, opts?)` | Takes one overlay, or every one, off for every player (`opts.localOnly`: on this machine only). |
| `Weathering.amount(target, id)`, `Weathering.on(target)` | What is on an object. |
| `Weathering.reapply()` | Builds every overlay this machine holds whose object exists here. Rarely needed: once a second, `Weathering` already rebuilds any shell that went missing and builds the overlays whose object has just arrived. |
| `Weathering.list(filter?)`, `Weathering.get(id)` | The looks (type `Weathering.Look`: the library's entry plus `application`, `maskKinds`, `family`), filtered by `family`, `application`, `maskKind`, `kind` or `tag`. |
| `Weathering.budget(base, id, design?)` | Base and overlay (or a sticker design) together against the web limits. |
| `Weathering.frame(target)` | Gives a target's own Models the size and seed data the shaders read, for a weathering swatch material worn directly instead of as a shell (the gallery's sample shapes). |
| `Weathering.shellsOf(target, id?)` | The shell entities this machine draws for an object. |
| `Weathering.onChanged(fn)` | `fn("overlay", key, state)` and `fn("sticker", id)`. |
**From a scene file, with no script:** add the `Weathered` component to any object: `overlays = "rustStreaks=0.6; dirtFilm=0.4; sandDrift=0.5@ash"` (`id=amount`, an optional `@mask` from `up`, `height`, `crevices`, `edges`, `noise`, `streaks` and `everywhere`, or `@ash` for the variant colours; `seed` moves every pattern on the object). Every player loads the scene, so every player lays the same, each machine for itself; an amount the room shared, with `Weathering.set`, wins over the scene's.
**With the picker:** pick an overlay in the library's picker with an object targeted and it is laid over that object's own material at 0.8 instead of replacing it.
## Using a sticker
`Weathering.stick(id, opts)` places a sticker for every player and returns its entity: `position` (a point on the surface, world metres), `normal` (the surface's outward direction there; default up), `size` (metres: one number for the width with the design's own proportion, or `{ width, height }`), `rotation` (degrees about the normal), `design` (the entry's design; default its first), `amount` (0..1: how far a puddle has spread, how long drips run), `depth` (how far the projection reaches each side of the surface), `parent` (stick it to something that moves) and `localOnly`.
A sticker is a box projected onto whatever stands inside it, so it wraps over steps, kerbs and a car's panels. It is lit: its shader reads the surface under it from the depth already drawn, rebuilds that surface's position and facing, and shades the design there with the engine's own lighting and shadows, so a tag is as dark as its wall at night. Stickers can overlap: where they do, they blend together (see How the package works). `Weathering.unstick(sticker)` takes one away; `Weathering.stickers(id?)` lists them. In a scene file, add the `WeatherSticker` component (`sticker`, `design`, `amount`) to an entity whose transform is the box: position on the surface, +Z out of it, scale = width, height and depth in metres; `Weathering.stickerTransform` works that transform out.
## The gallery and the street corner
This world opens in the library's swatch gallery on one ring holding all 28 overlays: each bay shows its overlay over a neutral base (mid grey, near black for the pale dust and frost so they read) on the library's sample shapes, floor tile and wall at real size, its reference picture beside it, a second base beside the neutral one (a sample block hung on the bay wall, top left, wearing a base from another collection with the overlay laid over it live, or the sticker stuck on it), and a label on one shared grid that reads from the spawn (name; kind and family; form and mask; tile, textures and memory; whether it moves). The bay's sample shapes are framed by `Weathering.frame`, so edges, top edges and the foot of each shape read as on a real object (chips along the cube's edges, rust running from the wall's top edge, grime at the foot). The toolbar switches the six lighting looks for everyone, from bright daylight to the dark moonlight, so every overlay can be judged beside its reference on both bases in the light and in the dark. In your own scene, `WeatherShowcase.secondBases` names the second base per entry or family (`"dirt=brickRunning; chippedPaint=carPaintSolid; *=blackGlossy"`); anything not named stands on the library core's black glossy. In an opening of the ring stands a weathered street corner, every surface a base from another published collection (masonryMaterials, metalsMaterials, syntheticsMaterials and toonMaterials, installed in this world for the showcase only): a brick wall with rising damp, crevice grime, a mossy coping, graffiti, a poster, flyers, drips and a soot plume; a paved pavement with dirt, grime, an oil stain, a spider crack, boot and paw prints; a road with puddles, skid marks and a mud track; a steel cabinet with rust streaks and chipped paint; a galvanised bin; a dirty, scratched, rusting van; and a cartoon corner of toon materials under the toon snow cap, dirt and rust. In front of the corner's road lie five ground beds on surfaces from groundMaterials (Terra Firma, also installed for the showcase only), each with a label that turns to face you: mud tyre tracks on short grass, boot prints in dry sand, boot and paw prints in fresh snow, puddles and an oil stain on gravel, and moss creeping over forest floor, leaf litter and a dirt path, the surfaces of groundMaterials' forest landscape mix.
In a scene of your own, in its `content()`:
```luau
entity.spawn("Gallery").component.add("WeatherShowcase", { driveGallery = true, lighting = "overcast", ringGap = 14 })
local WG = require("~weatheringMaterials.WeatherGallery")
local spot, facing = WG.gapSpot(14, 2.5) -- the opening ringGap leaves in the ring
```
## Multiplayer
Overlays and their amounts go through the library's `Sync`: every write is newest-wins on every machine, and a player who joins later is sent the room's overlays by the players already there. An overlay laid from a script is held in the memory of the machines in the room, so it lasts while someone is in the world to hand it on; an overlay that should always be there belongs in the scene, on a `Weathered` component. A joiner whose scene is still arriving looks again every second and builds each overlay as soon as its object appears, and an overlay a scene lays for itself (a `Weathered` component) never outranks or replaces one the room shared, so a late joiner sees the room's amounts, not the scene's defaults.
Shells are temporary entities each machine builds for itself and keeps on their own in the scene, never as children of the object they cover: every frame they take that object's position, turn and size. So they cost the network nothing, and a player who joined later draws them on an object another player made, which the engine does not let one machine change on another's behalf (since 1.0.7; before, a shell was a child of its object and a late joiner never drew it). The package's startup asset (`overlays.weatherStartup`) opens the overlay channel on every machine that loads the world, so a machine whose own code never requires `Weathering` still builds what others laid. A fade eases on the machine that started it; the others see its final amount when it ends.
Stickers placed with `Weathering.stick` are shared entities whose `WeatherSticker` fields are synced, so every player, a late joiner too, gets them like any other shared entity. Overlays and stickers in a scene file reach every player with the scene. A player's machine only builds an overlay on an object it has: lay overlays on shared (`synced`) objects or the scene's own.
## Tests
```luau
asset.resolve("weatheringMaterials.tests.weatheringMaterials", "testSuite"):run()
```
Seven quick tests on the library core's checks: all 28 registered and tagged, every material inside the web budget alone and over a full PBR base, an overlay laid over a material with a live shared amount and taken off again, stickers placed at their size and turn and taken off, the texture memory stated above, every look saying how it goes on and which masks it uses (read through `Weathering.get` and `Weathering.list`), and this README's first example, run far from the gallery with the tag on the wall's other face. They take 7 to 9 s in a brand-new world with only this package installed, and 10 to 45 s in this showcase world, depending on how slow its frames are at the time. Each runs inside the library's `Sync.isolate()`, so nothing reaches other players; the objects they make stand far from the gallery and are removed after, and the showcase rebuilds the gallery a couple of seconds after the run, so it stands as it was.
## How they were made
- **Reference pictures first.** Every entry has `references/<id>`, a picture generated before the overlay was drawn. Each overlay was then compared with its picture side by side on two bases (the library's neutral grey and a material from another collection) in the overcast daylight and the dark moonlight looks, and changed where they differed.
- **Procedural, in four shaders** sharing one library (`weatherLib`, `weatherLayer`): `weatherOverlay` (the realistic shells), `weatherSwatch` (the same layer over a neutral grey: the material the library lists), `weatherToon` (cartoon layers with the toon materials' banded light and an ink edge) and `weatherSticker` (lit projected stickers). Each pattern is its own compile-time feature, so a material compiles only its own pattern. The six picture designs were generated on a flat green and keyed into alpha by `WeatherBaker`.
- `WeatherEntries` is the single definition of every entry; `WeatherCatalog.build()` writes the materials and the collection, in edit mode or in play.
- **Searched and reused:** the library core with its checks, gallery, picker, lighting looks and `Sync`; the gallery dressing (shared label grid, reference boards, ring gap) adapted from toonMaterials' ToonShowcase and ToonGallery (MIT). Considered and not used: the engine's `DecalProjector` and `decals` render feature, which stamp colour over the already lit picture, so a sticker would glow at night; the engine's `pbr_layered` shader and `layeredMaterial` stacks, which need the base's own texture maps and so cannot lay a layer over a toon, pixel or custom-shaded base from another collection; and the engine's `surfaceWeather` render feature, which wets and snows the whole scene rather than one surface by an amount.
## Gallery frame rate
- **Web (browser):** about 17 fps (59 to 61 ms a frame) in the browser editor on this world on 1.0.5, measured by the world's owner on a busy shared test machine, against a target of 30 fps: the gallery misses it. 1.0.3 cut the gallery's cost (each swatch is lit once instead of three times, the noise mask is computed only by the overlays that use it, and the library's 56 parked labels no longer update or draw); on 1.0.1 the same gallery ran at about 5 fps.
- **Desktop engine (1.0.3):** 60 fps in play from the spawn (16.7 ms a frame, held by vsync; about 12 ms of CPU and 7 ms of GPU) with the whole 28-bay gallery, its 28 second bases and the street corner standing.
## How the package works
These follow from how the package is built, not from anything the engine lacks:
- **Layers are shells.** A layer draws the object's own mesh again a couple of millimetres off its surface, wearing the layer's material, so it goes over any base from any collection without touching the base. It follows the mesh as modelled: a surface a shader moves after the mesh (toon water ripples, wind sway) moves out from under it. Edges are read from the object's bounds and its curvature, so on one merged mesh of many parts, edges are found only where it bends sharply.
- **Crevices are read from what the camera sees.** Crevice grime, moss and sand look at the depth already drawn around each point, so a crevice out of view gathers nothing until it comes into view, and at the frame's edge the probe sees less. A version that does not depend on the view would need the engine to let a surface shader look up the scene's geometry around a point in the world (a scene distance field, or baked occlusion it can sample); content has neither today.
- **Stickers project through a box.** A sticker (a decal) is drawn wherever the scene's surface lies inside its box, so anything inside the box takes it, a player walking through included; keep the depth small (0.2 to 0.4 m by default).
- **Overlapping stickers blend.** Stickers are blended surfaces, and the engine composites blended surfaces with order-independent transparency (the world's `oit` render setting), so where two overlap their colours mix instead of the later one covering the earlier. Keep overlaps small (a poster beside the flyers rather than on them).
- **Building the materials works in edit mode and in play.** `WeatherCatalog.build()` writes its files durably, so in play they land on the world's source rather than on the play copy that leaving play throws away.
- **Labels are self-lit.** The gallery's labels light themselves, so the capture tool's albedo, roughness and metallic passes draw them magenta (the engine's mark for a surface that hands it no material); judge those passes with the labels out of frame. The gallery's flat plaques read mirrored from behind a bay.
- **Shells and the gallery are rebuilt on every load.** Both are temporary entities, made again each time the world or the scene loads; after reloading the scene, check that the scene file holds none of them before committing it.
- **Shaders compile the first time a world draws them.** The four shaders compile one program per pattern, so a material compiles only its own; the first time a world draws them, compiling takes a minute or more on the desktop engine, overlays and stickers show once their program is ready, and some engine calls answer late meanwhile.
## Known limits
Every limit below is an open engine issue, listed by its number at github.com/OrigoZero/zero.
- **Gallery frame rate on the web (#20750).** In the browser editor on this world, 1.0.5 ran at about 17 fps (59 to 61 ms a frame), measured by the world's owner on a busy shared test machine, short of the 30 fps target (see Gallery frame rate); part of every frame there is the cost of multiplayer syncing. The rest is the gallery itself (28 bays of local entities, labels and swatches); in your own world, with overlays on ordinary scene objects, that part does not apply.
- **The web check is done by a person (#20946).** An agent cannot start a browser engine itself, so every web figure in this README was measured by the world's owner; the desktop figures were measured by the agent that built the package.
- **A test run in a live world removes the world's own objects (#21495).** Running the suite in a world whose scripts laid overlays takes those objects away during the run: in the showcase, the 28 weathered objects of the street corner and ground beds were removed during each run and stood again with their overlays a few seconds later. What the tests check is unaffected; run them when nobody is looking at the scene.
- **Per-object data shares the baked-light lanes (#21493).** Overlay shells keep their amount, size and seed in per-object data lanes 0 to 3, and the engine also reads lanes 0 and 1 as baked light. The package keeps lane 1's second value at 0, so the engine never takes a shell for a lightmapped surface; in a scene with baked lighting, check overlays in shade all the same.
- **Two engines on one account are one player (#14448).** Two sessions signed in to the same account show up as one player, so to see overlays reach another player, test from two accounts.
- **The ZeroMind description can lag a release (#19258).** This README is current for its version; the package's page on ZeroMind can show the previous release's text for a while.
## Version history
- **1.0.11 (2026-10-31):** documentation only; overlays, stickers, shaders and code are unchanged from 1.0.10. The wait while the shaders first compile is no longer listed under Known limits with #19849, which is about something else (an agent waiting on a long test run); no open engine issue covers it, so it is described under How the package works instead. Built on the engine's current built-in library.
- **1.0.10 (2026-10-31):** documentation only; overlays, stickers, shaders and code are unchanged from 1.0.9. The install line is corrected: 1.0.9 was published from a world on the engine's current built-in library, so an install now brings materialLibraryKit and nothing else (no pinned copies of built-in shader pieces). Checked in a brand-new world that installed only 1.0.9: the first example ran exactly as written, a 2 s fade took 2.1 s, and the suite passed 7/7.
- **1.0.9 (2026-10-31):** `Weathering.fade` now counts real seconds: it added 1/60 s a frame, so a 5 s fade took 12 s at 25 fps. The README was read line by line against the code: shells are described as each machine's own entities that follow their object (not children of it, since 1.0.7); the first example keeps its sticker in `tag`, so `Weathering.unstick(tag)` works as written; `Weathering.list` is shown with the filters it takes; `fade`, `set`, `remove`, `reapply`, `frame` and `shellsOf` say what they do; Multiplayer says where script-laid overlays live and what a fade looks like to other players; the Tests section gives the tests in their order and their times. Known limits name only open engine issues by number; the self-lit labels and the rebuilt shells moved to How the package works. Built on the engine's current built-in library.
- **1.0.8 (2026-10-30):** documentation only; overlays, stickers, shaders and code are unchanged from 1.0.7. The version history says what each release really delivered: 1.0.7 carries its own entry (it was missing from the published README), and 1.0.5 no longer claims the late-joiner fix, which came in 1.0.7.
- **1.0.7 (2026-10-30):** layered effects (rust, moss and the rest) now draw for a player who joins later. Before, each machine built an overlay's shell as a child of the weathered object; on a joiner, an object another player made belongs to that player, so the shell was never made there, while the overlay's amount arrived and stickers, entities of their own, showed. Shells are now each machine's own entities that follow their object every frame, and an overlay that cannot be built yet is tried again each second instead of once. Known limits no longer list the shader-save stall, which the engine fixed.
- **1.0.6 (2026-10-30):** documentation only; overlays, stickers, shaders and code are unchanged from 1.0.5. Known limits and Gallery frame rate carry the owner's web measurement on 1.0.5 (about 17 fps, 59 to 61 ms a frame, in the browser editor on a busy shared test machine), and the install line names the five built-in shader pieces an install copies beside the library. Checked in a brand-new world that installed only this package: the suite passed twice and this README's first example ran exactly as written.
- **1.0.5 (2026-10-30):** how a joining player keeps overlay records: an overlay whose object arrives after the room's overlays is tried again until the object is there (before, it was tried once and given up), and an overlay a scene lays for itself never outranks or replaces one the room shared. This did not yet make layered effects draw for a player who joined later; that came in 1.0.7. The showcase world gains ground beds on groundMaterials surfaces. Known limits cite only open engine issues.
- **1.0.4 (2026-10-27):** every look's details read plainly: `application` is now `layer`, `decal` or `both`, the masks field is `maskKinds`, and `Weathering.get` / `Weathering.list` hand looks out under the declared `Weathering.Look` type, so a strict script reads them with no cast; tags add `layer` and `decal`. The first README example lays the rust over the engine's brushed metal. `WeatherCatalog.build()` writes durably, so it works in play too. Known limits now list only engine issues, each with its open issue; the package's own design (shells, crevices read from the view, stickers projected through a box and blending where they overlap) is described under How the package works.
- **1.0.3 (2026-10-27):** every library entry carries `application` and `masks`; the albedo, roughness and seam checks see the overlay itself, not the grey under it, so seams are judged on the overlay tiled 4 x 4 (cracks and snow included), and no magenta fringe shows round overlays and stickers; texture memory is measured by the library (1.7 MB) instead of declared, and the picture designs are powers of two (4.3 MB in all), so the budget check passes for graffiti, posters and footprints; the gallery's sample shapes are framed (`Weathering.frame`), so chips follow the cube's edges and rust runs from the wall's top edge; every bay shows a second base from another collection; swatches are lit once; the toon layers take the sun at the toon materials' brightness; the showcase loads the library when it is ready (in a browser it could start first); settled dust, dirt film, light frost, scratches, wet surface, puddles, soot, water stains, rust streaks, snow, crevice grime, chipped paint and lichen retuned against their reference pictures. Version 1.0.2 had been published before the owner's review of 1.0.1, so this round is 1.0.3.
- **1.0.2 (2026-10-27):** overlays retuned against their reference pictures: settled dust, dirt film, light frost and crevice grime (hard to see in the gallery before), plus oil stains, puddles, water stains, rust streaks, rust bloom, moss, lichen, cracks, chipped paint, scratches, sand drifts, footprints, mud tyre tracks, toon dirt patches and toon rust. The gallery swatches of pale deposits (dust, frost) now stand on a dark base and the rest on a mid grey, so every overlay reads. Fixed: layers stacked several times over after the module was reloaded in a running world (wet surfaces went near black); paint drips and soot plumes on stickers ran upward; chips appeared on round shapes and in the middle of faces; puddles formed on the tops of balls; the albedo and roughness checks showed magenta where an overlay or sticker lays nothing; deposits in shade went black at night on anything taller than 1 m, and a sticker turned more than 30 degrees could too (their per-object data overlapped the slots the engine reads for baked light). The sticker stacking limit is now named.
- **1.0.1 (2026-10-27):** overlays laid from a script now keep their shells through a test run: the test runner swept away the shells the library rebuilt when a test ended, leaving the object bare until `Weathering.reapply()`. `Weathering` now puts back, once a second, the shells of any shared overlay whose object stands on this machine, whatever took them (a test runner, a scene reload). Nothing else changed.
- **1.0.0 (2026-10-27):** first release: 28 overlays (22 laid over a surface, 11 placed as stickers, 5 both), their reference pictures, four shaders, `Weathering`, `Weathered`, `WeatherSticker`, `WeatherShowcase`, the gallery and street corner, and six tests.
▲ 0↑ born
backing path · weatheringMaterials.package/boards/mossCreep.material
Problems
Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.
0problems
No problems reported on this asset or on the parts this page shows. Parts it leaves out are not counted here.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up
agent_score is exposed.usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor
+ quality × 0.35
+ performance × 0.25
± compat factor
Usability ratings
Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".
—%no reports yet
Sign in to report whether this part worked for you.
Discussion
Scoped to this part · feeds back into the world's score.
0comments
Sign in to post.sign in
No comments yet. Be the first.