Python atlas_pack.py was a stop-gap substitute while davoryn was
down (preventing pull of the real node baker). With sporel-tool-
atlas-baker v0.2.0 shipping padding + blob-14 schema validation +
collision sidecar pass-through + alpha-analysis opaque flag, the
Python substitute is no longer needed.
Changes:
- scripts/atlas_pack.py removed
- scripts/bake_testbench.py: drop atlas_pack import + pack() call;
now produces source PNGs only. Node baker does the packing.
- scripts/bake_singles.py: same simplification, plus copies the
shared blob_rectilinear collision.json into each single-material
source dir (so atlas-baker E2 validation finds the sidecar).
- scripts/bake.sh: rewritten to call the node baker for all 8
atlases. Single-material variants (4 base styles + 3 tinted
singles) bake with `--schema blob-14` for E1+E2 validation.
Mixed-material blob_testbench bakes without --schema (42 tiles
don't match the canonical 14-slot pattern).
- 3 collision.json copies committed under assets/_sources/blob_
rect_{stone,grass,wood}/ — same content as the shared
blob_rectilinear one (since these single-material atlases share
the same blob geometry).
Single codepath for atlas production now: node baker only. No more
two-tool drift. Future-Python-changes to source generation auto-
flow through to baked atlases via bake.sh.
End-to-end verification: bake.sh produces 8 atlases cleanly; all
single-material bakes pass --schema blob-14 + collision validation;
opaque flag set on slot_13_solid (alpha-all-255), absent on the
other slots (transparent regions). SPOREL_CI=1 vagrant-skeleton
rc=0 with both blob_testbench (legacy multi-material) and
blob_rect_stone (vertex-painted single-material) loading correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lib-asset.prototype-blob-geom
Procedurally-generated reference assets for the Sporel blob autotile
scheme (S-V2E2-RM-Blob, 14-slot reduction). All sprites are simple
geometric primitives, drawn programmatically by the bundled Python
scripts. Intended as a starting template for human artists who will
produce real blob-material sprite sheets (stone, wood, water, ...) that
slot into the same 14-slot schema.
Version: 0.1.0
Lib-ID: lib-asset.prototype-blob-geom
Role: asset (pure data, no Lua code)
License: CC0 (procedural output, no authorship claim)
Requires: (none)
What's in here
prototype-blob-geom/
├── manifest.lib
├── LICENSE ← CC0
├── README.md ← this file
├── .gitignore
├── assets/
│ └── _sources/
│ ├── blob_rectilinear/ ← 14 raw 64x64 PNGs + collision.json
│ ├── blob_diagonal_cut/ ← (same slot count, different visual style)
│ ├── blob_organic/
│ └── blob_concave/
├── scripts/
│ ├── generate.py ← procedural writer (sprites + collision)
│ ├── reference_sheet.py ← per-style overview
│ ├── color_showcase.py ← runtime-tint demo
│ └── bake.sh ← regen everything + atlas-baker for all 4 styles
└── docs/
├── reference_sheet_rectilinear.png
├── reference_sheet_diagonal_cut.png
├── reference_sheet_organic.png
├── reference_sheet_concave.png
└── color_showcase.png
The 14-slot schema
Each tile reads its 8 neighbour cells. The neighbour-bitmask is clockwise from north, LSB-first:
| bit | direction |
|---|---|
| 0 | N |
| 1 | NE |
| 2 | E |
| 3 | SE |
| 4 | S |
| 5 | SW |
| 6 | W |
| 7 | NW |
A diagonal bit only counts if both adjacent cardinals are also set (blob-gating). After gating and reducing the 47 valid patterns under 90° rotation + mirror symmetry, you land on 14 canonical slots:
| Slot | Name | Canonical bitmask | Pattern (cardinals + diagonals) |
|---|---|---|---|
| 0 | isolated | 0b00000000 |
no neighbours |
| 1 | end | 0b00000001 |
1 cardinal (N) |
| 2 | corner_open | 0b00000101 |
2 adjacent cardinals (N+E), no diag |
| 3 | corner_full | 0b00000111 |
2 adjacent cardinals + diagonal (NE) |
| 4 | straight | 0b00010001 |
2 opposite cardinals (N+S) |
| 5 | tee_open | 0b00010101 |
3 cardinals (N+E+S), no diags |
| 6 | tee_half | 0b00010111 |
3 cardinals + 1 diag (NE) |
| 7 | tee_full | 0b00011111 |
3 cardinals + 2 diags (NE+SE) |
| 8 | cross_open | 0b01010101 |
4 cardinals, 0 diags |
| 9 | cross_q1 | 0b01010111 |
4 cardinals + 1 diag |
| 10 | cross_q2adj | 0b01011111 |
4 cardinals + 2 adjacent diags |
| 11 | cross_q2opp | 0b01110111 |
4 cardinals + 2 opposite diags (NE+SW) |
| 12 | cross_q3 | 0b01111111 |
4 cardinals + 3 diags |
| 13 | solid | 0b11111111 |
fully surrounded |
At runtime the engine (or atlas-baker, future M.2+) maps every one of the 47 raw blob patterns to one of these 14 slots by applying the canonical rotation + mirror needed to bring the raw pattern to its canonical form. Artists only ever draw the 14 base sprites.
Styles
All 4 styles share the same filled-sub-cell mask per slot (every 64×64 tile is conceptually a 4×4 grid of 16×16 sub-cells; the filled-sub-cell logic is identical across styles). They differ only in how external convex corners of the material region are rendered:
- The mini neighbour-diagram in the bottom-right of each cell shows which of the 8 surrounding cells share material with the centre cell.
- Slot 13 (
solid) is identical across all styles because there are no external convex corners when material continues seamlessly into all 8 neighbours. - All other slots have at least one external boundary corner, and the styles diverge there.
Rectilinear
Sharp 90° corners everywhere. Crisp, blocky, pixel-perfect look. Suits dungeon walls, stone floors, retro pixel art. Easiest to author custom art for — every sub-cell is just a filled rectangle.
Diagonal-cut
External convex corners chamfered with a 45° straight line. Gives an architectural, octagonal feel — slot 0 reads as an octagon, slot 1 as a "tab", slot 13 stays flat (no boundary). Good for isometric-feel walls, polished stone, art-deco trim.
Organic
External convex corners replaced with smooth quarter-arc curves (rounded-rectangle treatment). Gives a bauchy, flowing, blob-like feel. Suits water, moss, lava, slime, vegetation — anything that should look like it has surface tension.
Concave
External convex corners bitten with an inward quarter-disk. The material boundary at corners has concave indentations, giving a filigree, crystalline, spider-web feel. Suits ice frost, cracks, mycelium, arcane growths — anything that should look like it's reaching out fragilely.
Collision
Each style's source directory ships a collision.json describing the
per-slot collision footprint. Geometry is expressed as a list of
axis-aligned rectangles (row-wise + column-wise merged to the minimum
useful count). All four styles ship the same rectilinear collision;
for diagonal-cut / organic / concave the visible boundary deviates by
at most 5 px at convex external corners, which is acceptable for
typical gameplay collision. Pixel-perfect overrides are a future
refinement modders can ship per material.
Format (one entry per slot):
{
"schema": "blob-14",
"tile_size_px": 64,
"shape_kind": "rect_list",
"slots": [
{ "slot": 4, "name": "straight", "bitmask": "0b00010001",
"rects": [[16, 0, 32, 64]] },
{ "slot": 8, "name": "cross_open", "bitmask": "0b01010101",
"rects": [[16, 0, 32, 16], [0, 16, 64, 32], [16, 48, 32, 16]] },
{ "slot": 13, "name": "solid", "bitmask": "0b11111111",
"rects": [[0, 0, 64, 64]] }
]
}
Worst case (cross variants) is 3 rectangles; slot 13 collapses to a
single 64x64 rect; slot 4 to a single 32x64 vertical bar. The numbers
are tile-local pixels; the consuming engine translates by the tile's
world position. Friendly to Box2D / Chipmunk / AABB-tree physics
backends. Per the design paper §6 the atlas-baker (planned E2) will
fold these into a sibling tiles.collision.json next to the diffuse
atlas.
Color variations
The atlases ship in a single neutral stone-gray. Materials don't fork the atlas — they declare a colour tint at the material level, and the engine multiplies the atlas's per-pixel alpha against the tint at render time. One geometric atlas, N visual materials:
This is the production architecture: defining a new material is a
manifest-level declaration of {schema, style, tint, collision_policy},
not an asset fork. The bake-pipeline could in principle pre-tint
atlases for shader-free renderers, but for the M.2+ atlas format the
tint is applied at draw call time.
Tints used in the showcase above:
| Material | Tint |
|---|---|
| stone | #282C38 (default) |
| grass | #4A7838 |
| earth | #5C3D24 |
| wood | #8B6B3F |
| ice | #7BA8C8 |
Modders define their own palette in their module manifest.
How to use this lib
As a real consumer: add to module manifest's deps and
asset_aliases, choosing which style's atlas you want:
"deps": [
{"id":"lib-asset.prototype-blob-geom","version":"0.1.0"}
],
"asset_aliases": {
"blob_demo_v1": "lib-asset.prototype-blob-geom"
}
Maps reference slots by name (slot_05_tee_open) or the runtime selects
them by computed bitmask.
As a template for new art: copy the directory, rename to
lib-asset.<your-material-name>, then either:
- Hand-author 14 PNGs in
_sources/<your-pack>/, one per slot, using one of the reference sheets above as your style guide; OR - Modify
scripts/generate.pyif you want to keep the procedural approach for a different geometric primitive (e.g. a chunkier blocky style or a more aggressive organic curve).
The atlas-baker (scripts/bake.sh) packs your 14 PNGs into the runtime
atlas format — same pipeline as prototype-subterrain and every other
Sporel asset lib.
Regenerating
python scripts/generate.py # 14*4 = 56 base sprites + 4 collision.json
python scripts/reference_sheet.py # 4 reference sheets
python scripts/color_showcase.py # color showcase image
bash scripts/bake.sh # all of the above + atlas-bake all 4 styles
Dependencies: Python 3.10+ with Pillow; Node.js (for atlas-baker).
References
- Design paper:
sporel-meta/docs/design/2026-05-28-autotile-blob-styles-design.md - Boris the Brave, "Classification of Tilesets" — origin of the
S-V2E2-RM-Blobnotation - cr31, "Blob Tileset" — canonical 47-blob reference
- ADR-0005 (Reserved namespace prefixes —
lib-asset.*is not reserved, this is a community lib)
Notes
- License: CC0. This lib is safe for any context including
public-facing builds, unlike the
toBeDeletedprototype-asset libs (subterrain, fa-starter) that carry third-party licensing. - The output sprites are intentionally simple. Real game-grade art for the same schema should add: bevels, shadow under-tiles, texture noise, edge anti-aliasing, and per-style-profile collision polygons (see the design paper §6).
- The four styles ship as parallel atlases, not as a single combined atlas. A consumer module picks one style per material; mixing styles for the same material within one map would create visual incoherence.




