Initial commit — procedural blob-schema reference asset lib v0.1.0

Companion implementation to the design discussion at
sporel-meta/docs/design/2026-05-28-autotile-blob-styles-design.md §9.

Ships the S-V2E2-RM-Blob 14-slot schema in four canonical style
profiles, generated procedurally so the asset lib can be rebuilt
from scratch on any platform with Python 3 + Pillow:

  Styles (14 base slots each, sub-cell-grid layout):
    rectilinear   — crisp 90-degree corners (dungeon walls, retro)
    diagonal_cut  — 45-degree chamfered convex corners (iso-feel)
    organic       — rounded quarter-arc convex corners (water/moss)
    concave       — concave-bite convex corners (frost/web)

  Material variants (single-material atlases, one tinted set each):
    blob_rect_stone   — dark blue-gray (#282C38)
    blob_rect_grass   — mid green       (#4A7838)
    blob_rect_wood    — light brown     (#8B6B3F)

  Multi-material testbench atlas:
    blob_testbench    — 3 materials x 14 slots = 42 tiles in one
                        atlas, for the vagrant-skeleton showcase

Per-style collision.json (axis-aligned rect-list, row+column merged
to minimum form: slot 13 collapses to a single 64x64 rect, slot 4
to a single 32x64 vertical bar, cross variants max 3 rects).
Phase-1: same rectilinear collision shared across all 4 styles,
max 5 px deviation at corners; per-style polygons deferred to
atlas-baker E2.

Reference sheets at docs/reference_sheet_<style>.png show all 14
slots per style in a 7x2 grid with slot index + bitmask + 3x3
neighbour mini-diagram. Color showcase at docs/color_showcase.png
demonstrates that one atlas serves N visual materials via runtime
tint (5 sample colours).

Atlas packer (scripts/atlas_pack.py) is a pure-Python substitute
for sporel-tool-atlas-baker. It produces the Sporel-conformant
4-file atlas set (tiles.atlas.json + tiles.atlas.lock.json +
tiles.diffuse.atlas.png + 1x1 placeholder tiles.height.atlas.png)
with 2 px edge-replicated padding around every tile to prevent
bilinear sampler bleed (sister-tile colours leaking at UV
boundaries — the artefact that caused the green-flicker bug
caught during 0.5.0c integration with vagrant).

Generation pipeline (scripts/bake.sh):
  generate.py        -> 56 base sprites + 4 collision.json
  reference_sheet.py -> 4 reference_sheet PNGs
  color_showcase.py  -> color_showcase.png
  bake_testbench.py  -> 42-tile mixed-material atlas
  bake_singles.py    -> 3 x 14-tile single-material atlases

All output deterministic from the seed scripts (no random state).
.gitignore excludes the baked atlases (assets/atlases/) — those
are reproducible from sources via the bake scripts.

License: CC0 (procedural output, no third-party authorship claim).
Safe for any public-facing build.

Used in production by sporel-module-vagrant-skeleton via the
asset_aliases blob_testbench + blob_rect_stone — see the matching
commit there for the consumer-side integration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
calic
2026-05-28 20:51:44 +02:00
commit 247cdf0575
160 changed files with 2322 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
__pycache__/
*.pyc
assets/atlases/

12
LICENSE Normal file
View File

@@ -0,0 +1,12 @@
CC0 1.0 Universal (Public Domain Dedication)
The sprite assets in this lib are procedurally generated from simple
geometric primitives by the included Python scripts. No copyrightable
authorship is claimed.
To the extent possible under law, the author has waived all copyright
and related or neighboring rights to this work.
This work is published from: Germany.
https://creativecommons.org/publicdomain/zero/1.0/

260
README.md Normal file
View File

@@ -0,0 +1,260 @@
# 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.
![Rectilinear reference sheet](docs/reference_sheet_rectilinear.png)
### 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**.
![Diagonal-cut reference sheet](docs/reference_sheet_diagonal_cut.png)
### 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.
![Organic reference sheet](docs/reference_sheet_organic.png)
### 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.
![Concave reference sheet](docs/reference_sheet_concave.png)
## 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):
```json
{
"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:
![Color variations across 5 material tints](docs/color_showcase.png)
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:
```json
"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:
1. Hand-author 14 PNGs in `_sources/<your-pack>/`, one per slot, using
one of the reference sheets above as your style guide; OR
2. Modify `scripts/generate.py` if 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
```bash
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`](../../../sporel-meta/docs/design/2026-05-28-autotile-blob-styles-design.md)
- Boris the Brave, ["Classification of Tilesets"](https://www.boristhebrave.com/2021/11/14/classification-of-tilesets/) — origin of the `S-V2E2-RM-Blob` notation
- cr31, ["Blob Tileset"](https://www.cr31.co.uk/stagecast/wang/blob.html) — 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 `toBeDeleted` prototype-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.

View File

@@ -0,0 +1,270 @@
{
"schema": "blob-14",
"tile_size_px": 64,
"sub_cell_px": 16,
"shape_kind": "rect_list",
"_note": "Collision matches the rectilinear visual exactly. For diagonal_cut/organic/concave styles, the visible boundary deviates by up to 5 px at convex external corners \u2014 acceptable for typical gameplay collision. Modders needing pixel-perfect physics ship per-style polygon overrides.",
"slots": [
{
"slot": 0,
"name": "isolated",
"bitmask": "0b00000000",
"rects": [
[
16,
16,
32,
32
]
]
},
{
"slot": 1,
"name": "end",
"bitmask": "0b00000001",
"rects": [
[
16,
0,
32,
48
]
]
},
{
"slot": 2,
"name": "corner_open",
"bitmask": "0b00000101",
"rects": [
[
16,
0,
32,
16
],
[
16,
16,
48,
32
]
]
},
{
"slot": 3,
"name": "corner_full",
"bitmask": "0b00000111",
"rects": [
[
16,
0,
48,
48
]
]
},
{
"slot": 4,
"name": "straight",
"bitmask": "0b00010001",
"rects": [
[
16,
0,
32,
64
]
]
},
{
"slot": 5,
"name": "tee_open",
"bitmask": "0b00010101",
"rects": [
[
16,
0,
32,
16
],
[
16,
16,
48,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 6,
"name": "tee_half",
"bitmask": "0b00010111",
"rects": [
[
16,
0,
48,
48
],
[
16,
48,
32,
16
]
]
},
{
"slot": 7,
"name": "tee_full",
"bitmask": "0b00011111",
"rects": [
[
16,
0,
48,
64
]
]
},
{
"slot": 8,
"name": "cross_open",
"bitmask": "0b01010101",
"rects": [
[
16,
0,
32,
16
],
[
0,
16,
64,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 9,
"name": "cross_q1",
"bitmask": "0b01010111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 10,
"name": "cross_q2adj",
"bitmask": "0b01011111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
16,
48,
48,
16
]
]
},
{
"slot": 11,
"name": "cross_q2opp",
"bitmask": "0b01110111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
0,
48,
48,
16
]
]
},
{
"slot": 12,
"name": "cross_q3",
"bitmask": "0b01111111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
48
]
]
},
{
"slot": 13,
"name": "solid",
"bitmask": "0b11111111",
"rects": [
[
0,
0,
64,
64
]
]
}
],
"style": "concave"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

View File

@@ -0,0 +1,270 @@
{
"schema": "blob-14",
"tile_size_px": 64,
"sub_cell_px": 16,
"shape_kind": "rect_list",
"_note": "Collision matches the rectilinear visual exactly. For diagonal_cut/organic/concave styles, the visible boundary deviates by up to 5 px at convex external corners \u2014 acceptable for typical gameplay collision. Modders needing pixel-perfect physics ship per-style polygon overrides.",
"slots": [
{
"slot": 0,
"name": "isolated",
"bitmask": "0b00000000",
"rects": [
[
16,
16,
32,
32
]
]
},
{
"slot": 1,
"name": "end",
"bitmask": "0b00000001",
"rects": [
[
16,
0,
32,
48
]
]
},
{
"slot": 2,
"name": "corner_open",
"bitmask": "0b00000101",
"rects": [
[
16,
0,
32,
16
],
[
16,
16,
48,
32
]
]
},
{
"slot": 3,
"name": "corner_full",
"bitmask": "0b00000111",
"rects": [
[
16,
0,
48,
48
]
]
},
{
"slot": 4,
"name": "straight",
"bitmask": "0b00010001",
"rects": [
[
16,
0,
32,
64
]
]
},
{
"slot": 5,
"name": "tee_open",
"bitmask": "0b00010101",
"rects": [
[
16,
0,
32,
16
],
[
16,
16,
48,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 6,
"name": "tee_half",
"bitmask": "0b00010111",
"rects": [
[
16,
0,
48,
48
],
[
16,
48,
32,
16
]
]
},
{
"slot": 7,
"name": "tee_full",
"bitmask": "0b00011111",
"rects": [
[
16,
0,
48,
64
]
]
},
{
"slot": 8,
"name": "cross_open",
"bitmask": "0b01010101",
"rects": [
[
16,
0,
32,
16
],
[
0,
16,
64,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 9,
"name": "cross_q1",
"bitmask": "0b01010111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 10,
"name": "cross_q2adj",
"bitmask": "0b01011111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
16,
48,
48,
16
]
]
},
{
"slot": 11,
"name": "cross_q2opp",
"bitmask": "0b01110111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
0,
48,
48,
16
]
]
},
{
"slot": 12,
"name": "cross_q3",
"bitmask": "0b01111111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
48
]
]
},
{
"slot": 13,
"name": "solid",
"bitmask": "0b11111111",
"rects": [
[
0,
0,
64,
64
]
]
}
],
"style": "diagonal_cut"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

View File

@@ -0,0 +1,270 @@
{
"schema": "blob-14",
"tile_size_px": 64,
"sub_cell_px": 16,
"shape_kind": "rect_list",
"_note": "Collision matches the rectilinear visual exactly. For diagonal_cut/organic/concave styles, the visible boundary deviates by up to 5 px at convex external corners \u2014 acceptable for typical gameplay collision. Modders needing pixel-perfect physics ship per-style polygon overrides.",
"slots": [
{
"slot": 0,
"name": "isolated",
"bitmask": "0b00000000",
"rects": [
[
16,
16,
32,
32
]
]
},
{
"slot": 1,
"name": "end",
"bitmask": "0b00000001",
"rects": [
[
16,
0,
32,
48
]
]
},
{
"slot": 2,
"name": "corner_open",
"bitmask": "0b00000101",
"rects": [
[
16,
0,
32,
16
],
[
16,
16,
48,
32
]
]
},
{
"slot": 3,
"name": "corner_full",
"bitmask": "0b00000111",
"rects": [
[
16,
0,
48,
48
]
]
},
{
"slot": 4,
"name": "straight",
"bitmask": "0b00010001",
"rects": [
[
16,
0,
32,
64
]
]
},
{
"slot": 5,
"name": "tee_open",
"bitmask": "0b00010101",
"rects": [
[
16,
0,
32,
16
],
[
16,
16,
48,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 6,
"name": "tee_half",
"bitmask": "0b00010111",
"rects": [
[
16,
0,
48,
48
],
[
16,
48,
32,
16
]
]
},
{
"slot": 7,
"name": "tee_full",
"bitmask": "0b00011111",
"rects": [
[
16,
0,
48,
64
]
]
},
{
"slot": 8,
"name": "cross_open",
"bitmask": "0b01010101",
"rects": [
[
16,
0,
32,
16
],
[
0,
16,
64,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 9,
"name": "cross_q1",
"bitmask": "0b01010111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 10,
"name": "cross_q2adj",
"bitmask": "0b01011111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
16,
48,
48,
16
]
]
},
{
"slot": 11,
"name": "cross_q2opp",
"bitmask": "0b01110111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
0,
48,
48,
16
]
]
},
{
"slot": 12,
"name": "cross_q3",
"bitmask": "0b01111111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
48
]
]
},
{
"slot": 13,
"name": "solid",
"bitmask": "0b11111111",
"rects": [
[
0,
0,
64,
64
]
]
}
],
"style": "organic"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

View File

@@ -0,0 +1,270 @@
{
"schema": "blob-14",
"tile_size_px": 64,
"sub_cell_px": 16,
"shape_kind": "rect_list",
"_note": "Collision matches the rectilinear visual exactly. For diagonal_cut/organic/concave styles, the visible boundary deviates by up to 5 px at convex external corners \u2014 acceptable for typical gameplay collision. Modders needing pixel-perfect physics ship per-style polygon overrides.",
"slots": [
{
"slot": 0,
"name": "isolated",
"bitmask": "0b00000000",
"rects": [
[
16,
16,
32,
32
]
]
},
{
"slot": 1,
"name": "end",
"bitmask": "0b00000001",
"rects": [
[
16,
0,
32,
48
]
]
},
{
"slot": 2,
"name": "corner_open",
"bitmask": "0b00000101",
"rects": [
[
16,
0,
32,
16
],
[
16,
16,
48,
32
]
]
},
{
"slot": 3,
"name": "corner_full",
"bitmask": "0b00000111",
"rects": [
[
16,
0,
48,
48
]
]
},
{
"slot": 4,
"name": "straight",
"bitmask": "0b00010001",
"rects": [
[
16,
0,
32,
64
]
]
},
{
"slot": 5,
"name": "tee_open",
"bitmask": "0b00010101",
"rects": [
[
16,
0,
32,
16
],
[
16,
16,
48,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 6,
"name": "tee_half",
"bitmask": "0b00010111",
"rects": [
[
16,
0,
48,
48
],
[
16,
48,
32,
16
]
]
},
{
"slot": 7,
"name": "tee_full",
"bitmask": "0b00011111",
"rects": [
[
16,
0,
48,
64
]
]
},
{
"slot": 8,
"name": "cross_open",
"bitmask": "0b01010101",
"rects": [
[
16,
0,
32,
16
],
[
0,
16,
64,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 9,
"name": "cross_q1",
"bitmask": "0b01010111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
16,
48,
32,
16
]
]
},
{
"slot": 10,
"name": "cross_q2adj",
"bitmask": "0b01011111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
16,
48,
48,
16
]
]
},
{
"slot": 11,
"name": "cross_q2opp",
"bitmask": "0b01110111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
32
],
[
0,
48,
48,
16
]
]
},
{
"slot": 12,
"name": "cross_q3",
"bitmask": "0b01111111",
"rects": [
[
16,
0,
48,
16
],
[
0,
16,
64,
48
]
]
},
{
"slot": 13,
"name": "solid",
"bitmask": "0b11111111",
"rects": [
[
0,
0,
64,
64
]
]
}
],
"style": "rectilinear"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Some files were not shown because too many files have changed in this diff Show More